Script VersionCheck
return "common.qs 1.0.70";


//1.0.71 CW 15 Aug 2023 GetBatchID
//1.0.70 CW 10 May 2023 CreateGhostEx, StampHistoryEx
//1.0.69 CW 31 Mar 2023 WaferInfo has InSlotID
//1.0.68 CW 28 Mar 2023 Enhance LogClean
//1.0.67 CW  9 Feb 2023 Fix CTimer to allow user definable function
//1.0.66 CW 15 Jan 2023 Common for StampHistory and CTimer
//1.0.65 CW 15 Jan 2023 Source code synchronization
//1.0.64 CW 26 Oct 2022 Remove dependancy on QLog.dll/libQLog.so
//1.0.63 CW 10 Aug 2022 Remove GenerateCompact
//1.0.62 LZ 12 July 2022 DC kafka update 
//1.0.61 CW 8 July 2022 MES_screen_placed
//1.0.60 CW 29 Jun 2022	Send_RequestSync for SECS
//1.0.59 LZ 28 Jun 2022 SendLogToKafka add outrfid
//1.0.58 CW 24 Jun 2022 Try catch on 
//1.0.57 KL 26 May 2022 Added Capability
//1.0.57 CW 25 May 2022 Send_Reply for Secs interface, 
//1.0.56 CW 17 May 2022 Added optional OPCLog, HTTP use WaitSock@QSock
//1.0.55 CW 13 May 2022 Support for QS137
//1.0.54 CW 12 May 2022 Remove debug messages from Omron API
//1.0.53 CW 09 May 2022 Omron API
//1.0.52 CW 02 Apr 2022 Time format for log file is default to %Z (which include date)
//1.0.51 LZ 02 Apr 2022 Fixed a typo on 50
//1.0.50 CW 02 Apr 2022 Fixed a typo on 49
//1.0.49 CW 25 Mar 2022 Added tmpHTTPLock to secure SendAndWait_HTTP and SendToHTTP (Do not use)
//1.0.48 25 Mar 2022	Add config about http queue 
//1.0.47 CW 23 Mar 2022			Fix QSock Log intermittent start problem.  Map PrintLn (AppLog) and Print(AppLogNoLn)

//Added DeveloperStart support 1.0.4
//Added QSock 1.0.5
//Fixed bug in QSock 1.0.6
//1.0.7	Added GenerateCompact
//Added MES_WaferIn, MES_FlowIn, MES_FlowOut
//Fixed UDP ReceiveFrom bug
//1.0.9 Added size for FlowIn and FlowOut
//1.0.10 Fix UDP bug where Port is replaced with the sender's port 
//1.0.11 Need qs 1.0.86.  Must have try catch support
//1.0.13 SendUDPHost accidentally commented
//1.0.14/15 Handle HTTP connect failure
//1.0.17 GetGCount/GetGType
//1.0.21 Fix xx
//1.0.23 using foreach in Initialization (BUG)
//1.0.24 a Typo in HTTP_IP and HTTP_Port
//1.0.25 Added HTTP support for WaferStart
//1.0.26 Added HTTPLock for HTTP_SendAndWait
//1.0.27 fix an issue with sticky packet
//1.0.28 fix an issue with sticky packet, again
//1.0.29 fix an issue with sticky packet, again, again
//1.0.30 Check MES_FlowIn return code
//1.0.31 Update flow message
//1.0.32 send kafka log
//1.0.33 modify kafka
//1.0.34 modify generate mes waferid storage function
//1.0.35 add trycatch for kafka
//1.0.36 modify Kafka with queue
//1.0.37 merge dc.qs
//1.0.38 add http queue
//1.0.39 cut down print for nohup log size and change blockid
//1.0.40 fix waferstart message template format error,add ppid,modify GetValueByTrace for GetActualType is null issue
//1.0.41 add eidinfo to halm message
//1.0.42 remove ptkafka
//1.0.43 flowout add boat and zone info
//1.0.44 flowout add tube info
//1.0.45 HTTPServer
//1.0.46 fix ctime

EndScript

Script GetBatchIDEx
(prepend, s)
static String LastNS;
static U4 count;
String CurrentNS;

CurrentNS = StrTime("%Y%M%D_%h%m%s");
if (CurrentNS == LastNS)
{
	Inc(count);
}
else
{
	LastNS = CurrentNS;
	count = 0;
}

StrAssign(s, prepend, "_", LastNS, "_", count);

EndScript

Script GetBatchID
(prepend, s)

static U8 last = 0;			//Init last to 0
U8 current = CurrentFT();

if (current == last)
{
	current = current + 10000;
}
last = current;
StrAssign(s, prepend, " ", ToAikoTime(FT2QST(current)));
EndScript



Script SendAndWait_Helper
(engine, msg, rmsg)
I4 ret;

if (GetActualType(engine) == "String")
{
	return SendAndWaitEx@@Secs(:engine, msg, rmsg, QS_EID());
}
else
{
	return SendAndWaitEx@@Secs(engine, msg, rmsg, QS_EID());
}
EndScript

Script MSend
(engine, msg)
Send_Request(engine, msg);
EndScript

Script Send_RequestSync
(engine, msg, rmsg)

SecsMessage@Secs m;

if (GetActualType(msg) == "SecsMessage@Secs")
{
	return SendAndWait_Helper(engine, msg, rmsg);
}

if (GetSecsMessage(msg, m))
{
	return -1;
}

return SendAndWait_Helper(engine, m, rmsg);

EndScript

Script Send_Helper
(engine, msg, opaque, sb)
I4 ret;

if (GetActualType(engine) == "String")
{
	SendSecsMsg@Secs(:engine, msg, opaque, sb, ret);
}
else
{
	SendSecsMsg@Secs(engine, msg, opaque, sb, ret);
}
return ret;
EndScript

Script Send_Request
(engine, msg, op)

SecsMessage@Secs m;
I4 ret;

U0 opaque = 0;

if (GetCount(Parameter) >= 3)
{
	opaque = op;
}

if (GetActualType(msg) == "SecsMessage@Secs")
{
	return Send_Helper(engine, msg, opaque, 0);
}

if (GetSecsMessage(msg, m))
{
	return -1;
}

return Send_Helper(engine, m, opaque, 0);
EndScript

Script Send_Reply
(engine, msg, sb)
SecsMessage@Secs m;
I4 ret;
if (GetActualType(msg) == "SecsMessage@Secs")
{
	return Send_Helper(engine, msg, 0U0, sb);
}

if (GetSecsMessage(msg, m))
{
	return -1;
}

return Send_Helper(engine, m, 0U0, sb);

EndScript

Script GetSecsMessage
(name, m)
//m is supposed to be SecsMessage@Secs
U4 ret;
String s;
GetResource(name, s, ret);
if (ret)
{
	AppLog(ScriptName(), " failed to GetResource ", name);
	return ret;
}

Parse@Secs(m, s, ret);
if (ret)
{
	AppLog(ScriptName(), "Failed to parse", s);
}

return ret;
EndScript

Script COmron
(im, name)

String Name = name;
Map mapNode;
Object Sock;
CV OmronLock;

ObjCreateEx(Sock, "QSock");

if (QSO_Socket(Sock, 2, 2, 0))
{
	AppLog("QSO_Socket failed");
	return;
}
	
if (QSO_Bind(Sock, "0.0.0.0", 0))
{
	AppLog("QSO_Bind failed");
	return;
}

Constructor(im);

Script Constructor
(im)
Reference p;
U0 i;
String key;

foreach(p, im, i, key)
{
	MapAdd(mapNode, key, p);
}

EndScript

Script ReadU4Array
(alias, count, pu4)
String s, tmp;
VectorClear(pu4);
U2 f, b;
U4 u4;
U0 loop = 0;
if (ReadMultiWord(alias, count * 2, s) == 0 && StrLen(s) == count * 4)
{
	while (loop < count)
	{
		BinaryAssign(f, StrReverse(StrSubEx(tmp, s, loop * 4, 2)));		
		BinaryAssign(b, StrReverse(StrSubEx(tmp, s, loop * 4 + 2, 2)));
		VectorPush(pu4, (b << 16) + f);
		Inc(loop);
	}
	return 0;
}
return -1;
EndScript 

Script WriteU4Array
(alias, count, pu4)
//count is the number of U4

String s, tmp;

U2 f, b;
U4 u4;
U0 loop = 0;
while (loop < count)
{
	f = pu4[loop] & 0xFFFF;
	b = pu4[loop] >> 16;
	BinaryAssign(tmp, HiLoByte(f));
	StrAppend(s, tmp);
	BinaryAssign(tmp, HiLoByte(b));
	StrAppend(s, tmp);
	Inc(loop);
}

return WriteMultiWord(alias, count * 2, s);
EndScript 

Script ResetBit
(alias, bit)

U2 mask, u2;

if (bit < 0 || bit > 15)
{
	AppLog("Bad bit in ResetBit, ", bit);
	return -1;
}

mask = ~(1 << bit);

if (ReadWord(alias, u2) == 0)
{
	u2 = u2 & mask;
	return WriteWord(alias, u2);
}

return -1;

EndScript

Script SetBit
(alias, bit)

U2 mask, u2;

if (bit < 0 || bit > 15)
{
	AppLog("Bad bit in SetBit, ", bit);
	return -1;
}

mask = 1 << bit;

if (ReadWord(alias, u2) == 0)
{
	u2 = u2 | mask;
	return WriteWord(alias, u2);
}
return -1;
EndScript

Script ReadMultiWord
(alias, count, v)
//count is number of word to read
//v is String to accept the data

String ip = mapNode[alias][0];
I4 port = mapNode[alias][1];
I2 len = count;
String s;
U1 address = mapNode[alias][3];

StrAssign(s, "\x80\x00\x02\x00", ToStream(address), "\x00\x00\x70\x00\x00\x01\x01\x82", StrReverse(ToStream(ToI2(mapNode[alias][2]))), "\x00", StrReverse(ToStream(ToI2(count))));

if (SendAndWait(s, ip, port, v) == 0 && StrCmp(StrSub(v, 10, 4), "\x01\x01\x00\x00") == 0)
{
	StrReplace(v, 0, 14, "");
	return 0;
}

return -1;

EndScript

Script WriteMultiWord
(alias, count, v)
//count is the number of word to write
//v is String (length must be count x 2)

String ip = mapNode[alias][0];
I4 port = mapNode[alias][1];
I2 len = count;
String s;
U4 ret;
U1 address = mapNode[alias][3];

if (StrLen(v) != count * 2)
{
	return -1;
}

StrAssign(s, "\x80\x00\x02\x00", ToStream(address), "\x00\x00\x69\x00\x00\x01\x02\x82", StrReverse(ToStream(ToI2(mapNode[alias][2]))), "\x00", StrReverse(ToStream(ToI2(count))), v);
if (SendAndWait(s, ip, port, v) == 0 && StrCmp(StrSubR(v, 0, 4), "\x01\x02\x00\x00") == 0)
{
	return 0;	
}

return -1;
EndScript

Script WriteWord
(alias, u2)
String s;
BinaryAssign(s, HiLoByte(u2));
return (WriteMultiWord(alias, 1, s));
EndScript

Script ReadWord
(alias, u2)
String s;
if (ReadMultiWord(alias, 1, s) == 0)
{
	BinaryAssign(u2, s);
	u2 = HiLoByte(u2);
	return 0;
}
return -1;
EndScript

Script SendAndWait
(s, ip, port, v)

U4 event;
U0 i;
U4 sent_len;

U4 t0 = GetTick();
AppLog("[", Name, "] Sending ", StrLen(s), " bytes\x0A", StrHex(s, 1, 0));

CVLock(OmronLock);

if (QSO_SendTo(Sock, s, ip, port, sent_len))
{
	CVUnlock(OmronLock);
	AppLog(ScriptName(), " QSO_SendTo failed.  Time lapse is ", GetTick() - t0);
	return -1;
}
AppLog("[", Name, "] ", sent_len, " bytes sent");

if (QSO_Wait(Sock, 4, 3000, event))
{
	CVUnlock(OmronLock);
	AppLog(ScriptName(), " QSO_Wait failed.  Time lapse is ", GetTick() - t0);
	return -1;
}

if (event & 1)
{
	CVUnlock(OmronLock);
	AppLog(ScriptName(), " timeout waiting for response.  Time lapse is ", GetTick() - t0);
	return -1;
}

if (QSO_RecvFrom(Sock, v, 50000, ip, port))
{
	CVUnlock(OmronLock);
	AppLog("QSO_RecvFrom failed.  Time lapse is ", GetTick() - t0);
	return -1;
}
CVUnlock(OmronLock);

AppLog("[", Name, "] Received ", StrLen(v), " bytes.\x0A", StrHex(v, 1, 0));
AppLog("[", Name, "] completed succesfully.  Time lapse is ", GetTick() - t0, " ms");
return 0;

EndScript

EndScript


Script ToAikoTime
(cwt)

String s, s2;

StrAssign(s, cwt);
StrAssign(s2, StrSub(s, 0, 4),"-",StrSub(s, 4, 2),"-",StrSub(s,6,2), " ", StrSub(s, 8, 2), ":", StrSub(s, 10, 2), ":", StrSub(s, 12, 2), ".", StrSub(s, 14, 5));
return s2;

EndScript


Script Change
(ip, port)

:MES.IP = ip;
:MES.Port = ToU4(port);

EndScript

Script Init_Lib
(lib, alias)


String windows, linux;
U4 ret;
String v;

LibLoad(LinWin(StrAssign(linux,"./lib", lib, ".so"), StrAssign(windows,lib, ".dll")), alias, ret);
if (ret && ret != 4)		//for now at development stage, ignore the case where external lib is already loaded
{
	AppLog(ScriptName(), " " ,LinWin(linux, windows), " LibLoad for failed with ", ret);
	throw;		//lib failed to load
}

AppLog(alias, " init returns ", ret);
LibCall(alias, "Version", v);
PrintLn(v);

EndScript

Script LogSetHelper
(g, key, value)
if (QSO_Set(:g, key, value))
{
	AppLog("Failed to set ", key, " to ", value, ", at ", g);
}
EndScript

Script EntryScript

String s;
U4 ret;
U4 loop, n;

String FileName, FirstLine, TimeStamp, CloseName;
U8 MaxFileSize;
U4 TimeOption;
U4 DisableFlag;

String IP,URL;
U4 Port;
String tmpString;
String section_name;
Reference p, p0, p1, p2, p3;
U0 dummy_index, index;
String item_name, key;
U0 i;

GCreateEx("tmpHTTPLock", "CV");
GCreateEx("EAP", "Object");

if (GetJSONFile("eap.json", :EAP))
{
	AppLog("Failed to read 'eap.json'");
	return;
}

if (IFindEx(p0, :EAP, "Log") == 0)
{
	IMapPushExP(:EAP, "Log", "IMap", p0);
}

if (IFindEx(p1, p0, "AppLog") == 0)
{
	IMapPushEx(p0, "AppLog", "IMap");
}

if (IFindEx(p3, :EAP, "OPC") && p3)			//if OPC is enabled
{
	if (IFindEx(p1, p0, "OPCLog") == 0)
	{
		IMapPushExP(p0, "OPCLog", "IMap", p1);
		IMapPush(p1, "TimeOption", 2);
	}
	
	if (IFindEx(p1, p0, "MonitorLog") == 0)
	{
		IMapPushExP(p0, "MonitorLog", "IMap", p1);
		IMapPush(p1, "TimeStamp", "%Z,");
		IMapPush(p1, "FirstLine", "Time,ID,Alias,Value,Type\x0A");
	}
}


foreach(p1, :EAP["Log"], index, key)
{
	StrAssign(FileName, "Log/", key, "_%Y%M%D_%h%m.txt");
	FirstLine = "";
	TimeStamp = "%Z ";
	CloseName = ".log";
	MaxFileSize = 200000000;
	TimeOption = 3;
	DisableFlag = 0;
	
	foreach(p2, p1, dummy_index, item_name)
	{
		switch (item_name)
		{
		case "FileName"
			FileName = p2;
			break;
			
		case "FirstLine"
			FirstLine = p2;
			break;
			
		case "TimeStamp"
			TimeStamp = p2;
			break;
			
		case "CloseName"
			CloseName = p2;
			break;
			
		case "TimeOption"
			TimeOption = p2;
			break;
			
		case "MaxFileSize"
			MaxFileSize = p2;
			break;
			
		case "Disable"
			DisableFlag = p2;
			break;
		}
	}
	
	GCreateEx(key, "QLog");
	LogSetHelper(key, "Log file name", FileName);
	LogSetHelper(key, "Log first line", FirstLine);
	LogSetHelper(key, "Log time format", TimeStamp);
	LogSetHelper(key, "Log close extension", CloseName);
	LogSetHelper(key, "Log time option", TimeOption);
	LogSetHelper(key, "Log max size", MaxFileSize);
	if (DisableFlag == 0)
	{
		QSO_Start(:key);
	}
}

Sys_OutputAddScript("AppLog");

AppLog("Initializing ", :EAP["ToolID"]);


if (IMapCheck(:EAP, "Global"))
{
	foreach(p1, :EAP["Global"], dummy_index, s)
	{
		GCreateEx(s, p1);
	}
}

if (IFindEx(p0, :EAP, "GlobalLiteral"))
{
	foreach(p1, p0, dummy_index, s)
	{
		GCreate(s, p1);
	}
} 

if (IFindEx(p0, :EAP, "GlobalLiteral2"))
{
	foreach(p1, p0, dummy_index, s)
	{
		GCreate(s, LiteralParse(p1));
	}
} 

if (ScriptExist("DeveloperInit"))
{
	AppLog("Calling DeveloperInit");
	DeveloperInit();
	AppLog("Completed DeveloperInit");
}

if (IMapCheck(:EAP, "QSock"))
{
	Init_Lib("QSock", "QSock");		
}

if (IFindEx(p0, :EAP, "Thread"))		//Thread
{
	foreach(p1, p0, index, key)
	{
		PCallEx(key, p1);
	}
}

if (IMapCheck(:EAP, "HTTP"))
{
	Init_HttpQueue();
}

if (IFindEx(p, :EAP, "Omron"))
{
	AppLog("Init Omron started");
	foreach (p2, p, index, key)
	{
		GCreateEx(key, "COmron", p2, key);
	}
	AppLog("Init Omron ended");
}

Init_Kafka();

if (IFindEx(p0, :EAP, "KafkaProducer"))
{
	Init_Lib("QKafka", "QKafka");
	foreach(p1, p0, index, item_name)
	{
		tmpString = ifexp(IFindEx(p2, p1, "CallBack"))(p2)("DefaultKafkaCallBack");
		AppLog("[Debug] ", tmpString);
		AppLog("[Debug] Creating CKafkaProducer ", item_name);
		GCreateEx(item_name, "CKafkaProducer", item_name, tmpString);
		
		if (IFindEx(p2, p1, "Config"))
		{
			foreach(p3, p2, index, tmpString)
			{
				:item_name.SetConfig(tmpString, p3);
			}
		}
		:item_name.Start();
	}
	
}



if (IFindEx(p0, :EAP, "CHTTP"))
{
	AppLog("Found CHTTP Config");
	foreach(p1, p0, index, item_name)
	{
		AppLog("Configure ", item_name, " as CHTTP");
		TimeOption = 10000;			//set default time out 10 s
		foreach(p2, p1, dummy_index, key)
		{
			switch (key)
			{
			case "Remote IP"
				IP = p2;
				break;
				
			case "Remote port"
				Port = p2;
				break;
				
			case "url"
				URL = p2;
				break;
				
			case "Timeout"
				TimeOption = p2;
				break;
			}
		}
		GCreateEx(item_name, "CHTTP", item_name, IP, Port,URL, TimeOption);
		PCall("StartLoop", item_name);
	}
}

if (IFindEx(p0, :EAP, "CHTTPClient"))
{
	foreach(p1, p0, index, item_name)
	{
		TimeOption = 5000;			//set default time out 5s
		foreach(p2, p1, dummy_index, key)
		{
			switch (key)
			{
			case "Remote IP"
				IP = p2;
				break;
				
			case "Remote port"
				Port = p2;
				break;
				
			case "Timeout"
				TimeOption = p2;
				break;
			}
		}
		GCreateEx(item_name, "CHTTPClient", item_name, IP, Port, TimeOption, "HTTP/1.1");
	}
}
if (IMapCheck(:EAP, "SECS"))
{
	AppLog("Performing SECS Init");
	Init_Lib("QSECS", "Secs");
	Init_SECS(:EAP["SECS"]);
	AppLog("Completed SECS Init");
}

if (IMapCheck(:EAP, "MES"))
{
	if (IMapCheck(:EAP["MES"], "RemoteIP") == 0 || IMapCheck(:EAP["MES"], "RemotePort") == 0
	|| IMapCheck(:EAP["MES"], "HttpHost") == 0 || IMapCheck(:EAP["MES"], "HttpPort") == 0)
	{
		AppLog("Required RemoteIP or RemotePort not configured for MES");
		throw;
	}
	GCreateEx("MES", "CMES", :EAP["MES"]["RemoteIP"], :EAP["MES"]["RemotePort"], :EAP["MES"]["HttpHost"], :EAP["MES"]["HttpPort"]);
	PCall("StartLoop", "MES");
}

if (IFindEx(p0, :EAP, "CUpStream"))
{
	foreach(p1, p0, i, key)
	{
		GCreateEx(key, "CUpStream", key);
		:key.Set(p1);
	}
}

if (IFindEx(p, :EAP, "OPC") && p)
{
	Init_Lib("Open62541", "uac");	
	GCreateEx("MapOPC", "Map");
	GCreateEx("MapNode", "Map");
	GCreateEx("NodeCache", "Map");
	ConfigureOPC();
}



//add by 20220228 HTTPServer
if (IMapCheck(:EAP, "HTTPServer"))
{
	Init_Lib("QHTTP", "QHTTP");
	Init_HTTPServer();	
}

if (IFindEx(p0, :EAP, "CHTTPServer"))
{
	if (LibCheck("QHTTP") == 0)
	{
		Init_Lib("QHTTP", "QHTTP");
	}

	foreach(p1, p0, index, key)
	{
		GCreateEx(key, "CHTTPServer", key, p1);
	}
}

//add by 20220902
if (IMapCheck(:EAP, "HttpLink"))
{
	GCreateEx("httplinks","Map");
	foreach(p1, :EAP["HttpLink"], dummy_index, s)
	{
		MapAddEx(:httplinks,s,"HttpPathInfo",p1["ip"], p1["path"],p1["port"]);
	}
}


//Send version into to Kafka
if (ScriptExist("DeveloperStart"))
{
	DeveloperStart();
}
EndScript

Script HttpPathInfo
(x,y,z)
String ip,path;
I4 port;

ip=x;
path=y;
port=z;
EndScript

//add by 20220228 HTTPServer
Script Init_HTTPServer
U4 ret;
String LocalIP,LocalPort;

LocalIP=:EAP["HTTPServer"]["LocalIP"];
LocalPort=:EAP["HTTPServer"]["LocalPort"];

GCreateEx2("X", "HTTPServer", "QHTTP");

ConfigureHttpServer@QHTTP(:X, "Name", "X", ret);				//Name should be object name used in GCreateEx2 for the HTTPServer
ConfigureHttpServer@QHTTP(:X, "LocalIP", LocalIP, ret);
ConfigureHttpServer@QHTTP(:X, "LocalPort", LocalPort, ret);
ConfigureHttpServer@QHTTP(:X, "LogFileName", "Log/QHTTP.txt", ret);
ConfigureHttpServer@QHTTP(:X, "TimeFormat", "%Z ", ret);
ConfigureHttpServer@QHTTP(:X, "Handler", "HTTPServerHandler", ret);

StartHttpServer@QHTTP(:X, ret);
AppLog(ret);
EndScript

Script Init_HttpQueue
U4 ConsumerCount=1;
GCreateEx("hqcv", "CV");
GCreateEx("httpq", "Queue");
if(IMapCheck(:EAP["HTTP"],"Consumer"))
{
	ConsumerCount=ToU4(:EAP["HTTP"]["Consumer"]);
}
StartHTTPConsumer(ConsumerCount);
EndScript

Script Init_Kafka
//GCreateEx("MapKafkaWafer","Map");
EndScript


Script SecsSetHelper
(entity, key, value)

String tmp;
I4 ret;

GenericSet@Secs(:entity, key, StrAssign(tmp, value), ret);
if (ret)
{
	AppLog("[Error] Failed to set ", key, " to ", value, " for entity ", entity);
}

EndScript

Script Init_SECS
(secs)

AppLog(secs);

Reference p, p2;
U0 i, i2;
String key, key2, tmp;
U0 LogMaxSize;
String ConnectMode;

//Start the SECS connection
AppLog(ScriptName(), " Total connection configure is ", GetCount(secs));

foreach (p, secs, i, key)		//loop through the number of SECS Interface
{
	//Init the tool specific info, this is group under "SECS"
	AppLog(ScriptName(), " Starting SECS connection for ", key);
	
	GCreateEx2(key, ifexp(IFindEx(p2, p, "Type"))(p2)("HSMS"), "Secs");
	SetName@Secs(:key, key);
	LogMaxSize = 209715200;
	ConnectMode = "1";
	foreach(p2, p, i2, key2)
	{
		switch (key2)
		{
		case "Setup"			//for future expansion to support init routine
			break;
			
		case "Type"				//type is already handled above
			break;
			
		case "Log max size"		//EAP is setting a new default of 200 M, QSECS default is at 1G
			LogMaxSize = p2;
			break;
			
		case "Connect mode"		//Connect mode needs to specially handle because EAP could use "Active"/"Passive" instead of 1/0.  QSECS default is 1 (Active)
			switch (p2)
			{
			case 0
			case "Passive"
				ConnectMode = "0";
				break;
				
			case "Active"
				ConnectMode = "1";
				break;
			}
			break;
			
		default
			SecsSetHelper(key, key2, p2);
			break;
		}
	}
	SecsSetHelper(key, "Log max size", StrAssign(tmp, LogMaxSize));
	SecsSetHelper(key, "Connect mode", ConnectMode);
	Start@Secs(:key);
	AppLog(key, " started");
}

AppLog(ScriptName(), "Completed initialization");

EndScript


Script AppLog
String s;
Reference p;

foreach(p, Parameter)
{
	StrAppend(s, p);
}

if (MapCheck(Global, "AppLog"))
{
	QSO_WriteLn(:AppLog, s);
}
else
{
	PrintLn(s);
}

EndScript

Script OPCLog

String s;
Reference p;

if (MapCheck(Global, "OPCLog"))
{
	foreach(p, Parameter)
	{
		StrAppend(s, p);
	}
	QSO_WriteLn(:OPCLog, s);
}

EndScript

Script GLogLn
(name)
String s;
Reference p;

if (MapCheck(Global, name))
{
	IBegin(p, Parameter);
	while (IInc(p))
	{
		StrAppend(s, p);
	}
	QSO_WriteLn(:name, s);
}
EndScript

Script GLog
(name)

String s;
Reference p;

if (MapCheck(Global, name))
{
	IBegin(p, Parameter);
	while (IInc(p))
	{
		StrAppend(s, p);
	}
	QSO_Write(:name, s);
}

EndScript

Script HTTPConsumer
String out,ip;
U4 port;
CVLock(:hqcv);

Again:
	
	if (GetCount(:httpq))
	{
		out = QueueFront(:httpq)[0];
		ip = QueueFront(:httpq)[1];
		port = QueueFront(:httpq)[2];
		QueuePopFront(:httpq);
		CVUnlock(:hqcv);
		SendToHTTPWithQueue(out, ip, port);

		CVLock(:hqcv);
		goto Again;
	}
	else
	{
		CVWait(:hqcv);
		goto Again;
	}

EndScript

Script StartHTTPConsumer
(x)
//x is the number of consumer to start
U4 loop;
loop = x;
while (loop)
{
	PCall("HTTPConsumer");
	Dec(loop);
}
EndScript


Script SendToHTTP
(out, ip, port)
	
CVLock(:hqcv);

QueuePushBackEx(:httpq, "Vector");
VectorPush(QueueBack(:httpq), out);
VectorPush(QueueBack(:httpq), ip);
VectorPush(QueueBack(:httpq), port);
	
CVWakeAll(:hqcv);
CVUnlock(:hqcv);

EndScript

Script SendToHTTPWithQueue
(out, ip, port)
CVLock(:tmpHTTPLock);
SendToHTTPWithQueue_Ex(out, ip, port);
CVUnlock(:tmpHTTPLock);
EndScript

Script SendToHTTPWithQueue_Ex
(out, ip, port)

U4 ret;
U0 sent_len;
socket@QSock x;
U4 WriteInhibit = 1;
String s;
I4 event;
Vector v;

OpenSocket@QSock(x, 2, 1, 0, ret);
SetLogOption@QSock(x, "Log/HTTP/HTTP_%Y%M%D.log", "%Z ", 0, 0U8, 0, "", "");
StartLog@QSock(x);

BindIPv4@QSock(x, "0.0.0.0", 0, 1, ret);
if (ret)
{
	AppLog(ScriptName(), " - failed to bind to 0.0.0.0:0");
	return;
}

GetHostByName(ip, v);
if (GetCount(v))
{
	ConnectIPv4@QSock(x, v[0], port, ret);
	switch (ret)
	{
	case 10035
		WaitSock@QSock(x, event, 5000, QS_EID(), ret);
		AppLog("WSAWOULDBLOCK wait returns ", ret, ", event is ", event);
		break;
		
	case 0
		break;
		
	default
		AppLog("HTTP connect failed");
		return;
	}

	Send@QSock(x, out, sent_len, ret);
	if (ret || sent_len != StrLen(out))
	{
		AppLog(ScriptName(), " send failed");
		return;
	}

	XXX:
	WaitSock@QSock(x, event, 5000, QS_EID(), ret);
	if (ret)
	{
		AppLog(ScriptName(), " HTTP wait failure");
		return;
	}

	if (event & 1)
	{
		AppLog(ScriptName(), "Timeout waiting for response from HTTP server");
		return;
	}

	if (event & 4)			//read event
	{
		Receive@QSock(x, s, ToU0(1024), ret);			//receive 1024 bytes
		if (ret)
		{
			AppLog("Receive failed with ", ret);
			return;
		}
		AppLog("[HTTP] Server responded\x0A", s);
		return;
	}
	goto XXX;
}
else
{
	AppLog("Failed to resolve host name ", ip);
}

EndScript

Script SendAndWait_HTTP
(out, ip, port, R)
CVLock(:tmpHTTPLock);
SendAndWait_HTTP_Ex(out, ip, port, R);
CVUnlock(:tmpHTTPLock);
EndScript


Script SendAndWait_HTTP_Ex
(out, ip, port, R)

U4 RType = 0;			//0 for not defined, 1 for Content-Length, 2 for chunked, 3 for chunked while parsing body (body length is not yet at the required length)
String Header, Body;
String tmp, tmp2;
U4 ret, len, SplitFlag = 0;
U0 sent_len;
U0 content_length, offset = 0;
socket@QSock x;
U4 WriteInhibit = 1;
String s;
I4 event;
Vector v;
U0 pos, pos2;

OpenSocket@QSock(x, 2, 1, 0, ret);
SetLogOption@QSock(x, "Log/HTTP/HTTP_%Y%M%D.log", "%Z ", 0, 0U8, 0, "", "");
StartLog@QSock(x);

BindIPv4@QSock(x, "0.0.0.0", 0, 1, ret);
if (ret)
{
	AppLog(ScriptName(), " - failed to bind to 0.0.0.0:0");
	return;
}

GetHostByName(ip, v);
if (GetCount(v))
{
	ConnectIPv4@QSock(x, v[0], port, ret);
	switch (ret)
	{
	case 10035
		WaitSock@QSock(x, event, 5000, QS_EID(), ret);
		AppLog("WSAWOULDBLOCK wait returns ", ret, ", event is ", event);
		break;
		
	case 0
		break;
		
	default
		AppLog("HTTP connect failed");
		return;
	}

	Send@QSock(x, out, sent_len, ret);
	if (ret || sent_len != StrLen(out))
	{
		AppLog(ScriptName(), " send failed");
		return;
	}

	XXX:
	WaitSock@QSock(x, event, 5000, QS_EID(), ret);
	if (ret)
	{
		AppLog(ScriptName(), " HTTP wait failure");
		return;
	}

	if (event & 1)
	{
		AppLog(ScriptName(), "Timeout waiting for response from HTTP server");
		return;
	}
	
	if (event & 2)
	{}

	if (event & 4)			//read event
	{
		Receive@QSock(x, s, ToU0(4096), ret);			//receive 4096 bytes
		if (ret)
		{
			AppLog("Receive failed with ", ret);
			return;
		}
		
		if (StrLen(s) == 0)
		{
			AppLog("Error", "Read event is trigger but no data read");
			R = "";
			return;
		}
		
		if (SplitFlag)			//this indicate header is complete
		{
			switch (RType)
			{
			case 1
				StrAppend(R, s);
				if (StrLen(R) == content_length)
				{
					AppLog("Message with content length, ", content_length, " completed");
					AppLog(R);
					return;
				}
				break;
				
			case 2
				StrAppend(Body, s);
Chunked2_Again:
				if (StrFind(Body, "\x0D\x0A", offset, pos))
				{
					tmp = StrSub(Body, offset, pos);
					StrReplace(Body, offset, pos + 2, "");			//remove the line with number
					tmp2 = "";		//this is to counter a bug of StrRepeat, which do not clear the residue in assignee
					StrReplace(tmp, 0, 0, StrRepeat(tmp2, "0", 8 - StrLen(tmp)));
					BinaryAssign(len, StrConvertHex(tmp2, tmp));
					len = HiLoByte(len);
					if (len == 0)
					{
						AppLog("Chunked TX completed");
						AppLog(R);
						return;
					}
					
					if (StrLen(Body) >= len + 2)
					{
						StrAppend(R, StrSub(Body, 0, len));
						StrReplace(Body, 0, len + 2, "");
						goto Chunked2_Again;
					}
					RType = 3;		//waiting for more body data in chunked
				}
				break;
				
				
			case 3
				StrAppend(Body, s);
				if (StrLen(Body) >= len + 2)
				{
					StrAppend(R, StrSub(Body, 0, len));
					StrReplace(Body, 0, len + 2, "");
					RType = 2;
					goto Chunked2_Again;
				}
				break;
			}
			
		}
		else
		{
			StrAppend(R, s);
			if (StrFind(R, "\x0D\x0A\x0D\x0A", 0, pos))
			{			
				SplitFlag = 1;
				StrAssign(Header, StrSub(R, 0, pos));
				Body = StrReplace(R, 0, pos + 4, "");
				
				if (StrFind(Header, "Content-Length: ", 0, pos) && StrFind(Header, "\x0D\x0A", pos, pos2))		//Look for line with Content-Length 
				{
					RType = 1;
					content_length = StrSub(Header, pos + 16, pos2 - pos - 16);
					if (StrLen(Body) == content_length)
					{
						R = Body;
						return;
					}
				}
				
				if (StrFind(Header, "Transfer-Encoding: ", 0, pos) && StrFind(Header, "chunked", pos, pos2))		//Look for line with Content-Length 
				{
					RType = 2;
					R = "";
					
Chunked_Again:
					if (StrFind(Body, "\x0D\x0A", offset, pos))
					{
						tmp = StrSub(Body, offset, pos);
						StrReplace(Body, offset, pos + 2, "");			//remove the line with number, and also the 2 new line characters
						StrReplace(tmp, 0, 0, StrRepeat(tmp2, "0", 8 - StrLen(tmp)));		//pad 0 upto 8 characters
						if (StrConvertHex(tmp2, tmp))
						{
							AppLog("[Error] ", ScriptName(), " Unabled to convert hexadecimal number ", tmp);
							return;
						}
						BinaryAssign(len, tmp2);
						len = HiLoByte(len);
						if (len == 0)
						{
							return;
						}
						
						if (StrLen(Body) >= len + 2)
						{
							StrAppend(R, StrSub(Body, 0, len));
							StrReplace(Body, 0, len + 2, "");
							goto Chunked_Again;
						}
						RType = 3;
					}
				}
				else
				{
					R = Body;
					return;
				}
			}
		}
	}
	
	if (event & 8)		//write event
	{
		//PrintLn("Write event");
	}
	
	if (event & 16)		//close event
	{
		return;
	}
	
	if (event & 32)		//accept event
	{}
	
	if (event & 64)		//connect event
	{}
	
	goto XXX;
}
else
{
	AppLog("Failed to resolve host name ", ip);
}

EndScript

Script CMES
(ip, port, http_ip, http_port)

U8 txId = 0;
U8 LastTxTime = 0;
String msg_seq_ns;
U8 msg_seq;

U8 StartTime = FT2QST(CurrentFT());			//for HTTP Host
U8 Seq = 0;									//For HTTP Host

Queue OutQueue;
Map In;

socket@QSock socket;

String IP, HTTP_IP;
U4 Port, HTTP_Port;

CV cv;
			
IP = ip;
Port = port;
HTTP_IP = http_ip;
HTTP_Port = http_port;

msg_seq = 1;

Init();
//End of CMES Constructor


Script Init

U4 ret;
U8 u8;
String tmp;

CurrentFT(u8);
tmp = FT2QST(u8);
StrAssign(msg_seq_ns, StrSub(tmp, 0, 4), "-", StrSub(tmp, 4, 2), "-", StrSub(tmp, 6, 2), " ",
	StrSub(tmp, 8, 2), ":", StrSub(tmp, 10, 2), ":", StrSub(tmp, 12, 2), ".", StrSub(tmp, 14, 5));

OpenSocket@QSock(socket, 2, 2, 0, ret);

if (ret)
{
	AppLog("Failed to open socket for MES.  Error code ", ret);
	throw;
}

BindIPv4@QSock(socket, "0.0.0.0", 0, 1, ret);
if (ret)
{
	AppLog("Failed to bind to 0.0.0.0:0");
	throw;
}

AppLog("MES init OK");

EndScript

Script MessageLoop
(host)
U4 ret;
U4 event;
String s;
U0 sync;

String ip;
U4 port;

AppLog("MES MessageLoop started");

AAA:

WaitSock@QSock(socket, event, -1, QS_EID(), ret);

CVLock(cv);
while (GetCount(OutQueue))
{
	SendTo@QSock(socket, QueueFront(OutQueue), ToU8(2048), IP, Port, ret);
	QueuePopFront(OutQueue);
}
CVUnlock(cv);

if (event & 2)
{
	ResetUserEvent@QSock(socket);
}

if (event & 4)
{
	ReceiveFrom@QSock(socket, s, ToU8(2048), ip, port, ret);
	if (ret == 0)
	{
		PrintLn(ip, ":", port);
		PrintLn(s);
	}
}

goto AAA;
EndScript

Script GetNextTxID
(n)
CVLock(cv);
n = Inc(msg_seq);
CVUnlock(cv);
EndScript

Script HTTP_FlowInComplete
(car, qty, pos, mode, R, k, ppid)

String s, r;
String out;
U4 ret;
Object J;
GetResource("FlowInComplete", s, ret);
JSONParse(J, s, ret);

J[1] = ToAikoTime(FT2QST(CurrentFT()));
J[2][0] = car;
J[2][1] = :EAP["ToolID"];
J[2][2] = :k.K;
J[2][3] = :k.Mode;
if (GetCount(Parameter) >= 7)
{
	J[2][4] = ppid;
}
J[2][5] = qty;
J[2][6] = pos;
J[2][7] = :EAP["UndockMode"][mode];
J[2][8] = :k.Capability;
J[3][0] = ToAikoTime(StartTime);
J[3][1] = Inc(Seq);

JSONWrite(J, 0, s, ret);
if (ret)
{
	AppLog("[Error] ", ScriptName(), " Unable to perform JSONWrite");
	AppLog(J);
}

StrAppend(out, "POST /api HTTP/1.1\x0D\x0A", 
	"Content-Length: ", StrLen(s), "\x0D\x0A",
	"Host:",HTTP_IP,":",HTTP_Port,"\x0D\x0A",
	"EqpID: ", :EAP["ToolID"], "\x0D\x0A",
	"Content-Type: application/json\x0D\x0A\x0D\x0A",
	s);

CVLock(:HTTPLock);
SendAndWait_HTTP(out, HTTP_IP, HTTP_Port, R);
CVUnlock(:HTTPLock);

EndScript

Script HTTP_FlowIn
(car, k, R, ppid)

String s, r;
String out;
U4 ret;
Object J;
GetResource("FlowInEx", s, ret);
JSONParse(J, s, ret);
J[1] = ToAikoTime(FT2QST(CurrentFT()));
J[2][0] = car;
J[2][1] = :EAP["ToolID"];
J[2][2] = :k.K;
J[2][3] = :k.Mode;
if(GetCount(Parameter) >= 4)
{
	J[2][4] = ppid;
}
J[2][5] = :k.Capability;
J[3][0] = ToAikoTime(StartTime);
J[3][1] = Inc(Seq);

JSONWrite(J, 0, s, ret);
if (ret)
{
	AppLog("[Error] ", ScriptName(), " Unable to perform JSONWrite");
	AppLog(J);
}

StrAppend(out, "POST /api HTTP/1.1\x0D\x0A", 
	"Content-Length: ", StrLen(s), "\x0D\x0A",
	"Host:",HTTP_IP,":",HTTP_Port,"\x0D\x0A",
	"EqpID: ", :EAP["ToolID"], "\x0D\x0A",
	"Content-Type: application/json\x0D\x0A\x0D\x0A",
	s);

CVLock(:HTTPLock);
SendAndWait_HTTP(out, HTTP_IP, HTTP_Port, R);
CVUnlock(:HTTPLock);
EndScript

Script HTTP_FlowOut
(car, vWID, qty, k, R, info, ppid)
String s;
String out;
U4 ret,loop,count;
Object J;
Reference p;
GetResource("FlowOutEx", s, ret);
JSONParse(J, s, ret);
J[1] = ToAikoTime(FT2QST(CurrentFT()));
J[2][0] = car;
J[2][1] = ifexp(IMapCheck(:EAP, "eqp_blk"))(:EAP["eqp_blk"])(:EAP["ToolID"]);

if(IMapCheck(info,"data")&&GetActualType(info["data"]) <> "EOF")
{
	if(IMapCheck(info["data"],"wo_id"))
	{
		J[2][2] = info["data"]["wo_id"];//:k.WorkOrder;
	}
	J[2][3] = info["data"]["wo_dept_id"]; //deptid
	J[2][4] = info["data"]["prod_code"]; //deptid
	J[2][5] = info["data"]["tech"]; //tech
	J[2][6] = info["data"]["prod_id"];//id
	J[2][7] = info["data"]["prod_name"];//name
	J[2][8] = info["data"]["flow_id"];
	J[2][9] = info["data"]["flow_name"];
}

J[2][10] = qty;
J[2][11] = vWID;
if(GetCount(Parameter) >= 7)
{
	J[2][12] = ppid;
}
J[2][13] = :k.Capability;

J[3][0] = ToAikoTime(StartTime);
J[3][1] = Inc(Seq);

JSONWrite(J, 0, s, ret);
if (ret)
{
	AppLog("[Error] ", ScriptName(), " Unable to perform JSONWrite");
	AppLog(J);
}

StrAppend(out, "POST /api HTTP/1.1\x0D\x0A", 
	"Content-Length: ", StrLen(s), "\x0D\x0A",
	"Host:",HTTP_IP,":",HTTP_Port,"\x0D\x0A",
	"EqpID: ", :EAP["ToolID"], "\x0D\x0A",
	"Content-Type: application/json\x0D\x0A\x0D\x0A",
	s);
	
CVLock(:HTTPLock);
SendAndWait_HTTP(out, HTTP_IP, HTTP_Port, R);
CVUnlock(:HTTPLock);
EndScript


Script TX_FlowInComplete
(carrier, qty, last, undockmode, k, ppid)

String r;

switch (GetCount(Parameter))
{
case 6
	HTTP_FlowInComplete(carrier, qty, last, undockmode, r, k, ppid);
	break;
	
case 5
	HTTP_FlowInComplete(carrier, qty, last, undockmode, r, k);
	break;
}
EndScript


Script TX_FlowIn
(carrier, vWID, size, k, ppid)

String r;
U8 u8;
U4 ret, loop;
Object J, J2;
String s, tmp;
Vector V;
Reference p;
U0 index;
Vector tmpv;

HTTP_FlowIn(carrier, k, r, ppid);
JSONParse(J2, r, ret);
if(ret)
{
	AppLog("Bad JSON response received ", r);
	throw;
}

if (IMapCheck(J2, "data"))
{
	if(GetActualType(J2["data"]) != "IMap")
	{
		AppLog("[Fatal] data not valid in MES reply at ", ScriptName());
	}
	
	if(IMapCheck(J2["data"],"wafers"))
	{
		if(GetActualType(J2["data"]["wafers"])=="U0" || GetActualType(J2["data"]["wafers"]) == "EOF")
		{
			loop=0;
			while (loop < size)
			{
				:k.Generate(tmpv, 1);
				MapAdd(:mapCW, vWID[loop], tmpv[0]);
				AppLog("[FlowIn] Generated ", vWID[loop], "-", tmpv[0]);
				Inc(loop);
			}
		}
		else
		{
			if (GetCount(J2["data"]["wafers"]) == size)
			{
				loop = 0;
				while (loop < size)
				{
					MapAdd(:mapCW, vWID[loop], J2["data"]["wafers"][loop]);
					AppLog("[FlowIn] Added ", vWID[loop], "-", J2["data"]["wafers"][loop]);
					Inc(loop);
				}
			}
			else
			{
				if (GetCount(J2["data"]["wafers"]) < size)
				{
					loop = 0;
					while (loop < GetCount(J2["data"]["wafers"]))
					{
						MapAdd(:mapCW, vWID[loop], J2["data"]["wafers"][loop]);
						AppLog("[FlowIn] Added ", vWID[loop], "-", J2["data"]["wafers"][loop]);
						Inc(loop);
					}
					
					while (loop < size)
					{
						:k.Generate(tmpv, 1);
						MapAdd(:mapCW, vWID[loop], tmpv[0]);
						AppLog("[FlowIn] Generated ", vWID[loop], "-", tmpv[0]);
						Inc(loop);
					}
				}
			}
		}		
	}
}


HandleFlowInReply(J2,vWID);

EndScript

Script HandleFlowInReply
(J, vWID)

U8 curFT = CurrentFT();
Reference p;

CVLock(:CVHTTP);

ClearOldMESInfo();

while (MapCheck(:MapMESInfo, curFT))
{
	Inc(curFT);
}
MapAdd(:MapMESInfo, curFT, J);
AppLog("[MapMESInfo] Added ", curFT, ", size is ", GetCount(:MapMESInfo), "\x0A", J);

foreach(p, vWID)
{
	if(MapCheck(:MapMESKey, p))
	{
		AppLog("[MapMESKey] Wafer id ", p, " has entry in MapMESKey, value is ", :MapMESKey[p], ", updating to ", curFT);
		:MapMESKey[p] = curFT;
	}
	else
	{
		MapAdd(:MapMESKey, p, curFT);
		AppLog("[MapMESKey] Added ", p, "<->", curFT, ", size = ", GetCount(:MapMESKey));
	}
}

CVUnlock(:CVHTTP);
EndScript

Script TX_EAPStartUp
(k)
String R;
String s;
String out;
U4 ret;
Object J;
GetResource("EAPStartUp", s, ret);
JSONParse(J, s, ret);
J[1] = ToAikoTime(FT2QST(CurrentFT()));
StrAssign(J[2][0], "udp://");
J[2][1] = :EAP["ToolID"];
J[2][2] = :k.Mode;

J[3][0] = ToAikoTime(StartTime);
J[3][1] = Inc(Seq);

AppLog(J);

JSONWrite(J, 0, s, ret);
if (ret)
{
	AppLog("[Error] ", ScriptName(), " Unable to perform JSONWrite");
	AppLog(J);
}

StrAppend(out, "POST /api HTTP/1.1\x0D\x0A", 
	"Content-Length: ", StrLen(s), "\x0D\x0A",
	"Host:",HTTP_IP,":",HTTP_Port,"\x0D\x0A",
	"EqpID: ", :EAP["ToolID"], "\x0D\x0A",
	"Content-Type: application/json\x0D\x0A\x0D\x0A",
	s);
	

CVLock(:HTTPLock);
SendAndWait_HTTP(out, HTTP_IP, HTTP_Port, R);
CVUnlock(:HTTPLock);

EndScript

Script TX_FlowOut
(carrier, vWID, size, k, ppid)

//vWID should be type Vector

String r,kafkakey;
Vector v, tmpVec;

U8 u8,tickKey;
U4 ret,count = 0, loop = 0;
Object J;
String s, tmp,strTick;
String key;

PString pStrTick;

Reference p, pMESKey, pMESInfo, pLocal;
Map lMap;		//local Map to count the most number of wafer, key is FT
U0 index;
String iKey, tmpkey;

IMap flowInfo;
AppLog("FlowOut for ", carrier, ", quantity is ", size);
CVLock(:CVHTTP);

//routine to implement algo such that the FlowOut is using the MapMESInfo of the most wafers in that cassette
foreach(p, vWID, index)
{
	if (IFind(pMESKey, :MapMESKey, p) && IFind(pMESInfo, :MapMESInfo, pMESKey))		//need to computate
	{
		if (IFind(pLocal, lMap, pMESKey))
		{
			Inc(pLocal);
		}
		else
		{
			MapAdd(lMap, pMESKey, 1);
		}
	}
}

foreach (pLocal, lMap, iKey)
{
	if (count == 0 || pLocal > count)
	{
		key = iKey;
		count = pLocal;
	}
	AppLog("Count for key ", key, " is ", pLocal);
}

if (StrLen(key))
{
	AppLog("Key ", key, " is selected for FlowOut info");
	flowInfo = :MapMESInfo[key];
	AppLog(flowInfo);
}
else			//take the last FlowIn info
{
	AppLog("No FlowOut info available among the out wafers in casssete.  Will use current MES info");
	IEnd(pMESInfo, :MapMESInfo);
	if (IDec(pMESInfo))
	{
		flowInfo = pMESInfo;
		AppLog(flowInfo);
	}
	else
	{
		AppLog("No flowInfo is assigned");
	}
}

while (loop < size)			//cycle through
{
	if (MapCheck(:mapCW, vWID[loop]) && StrLen(:mapCW[vWID[loop]]))
	{
		VectorPush(v, :mapCW[vWID[loop]]);	
		MapRemove(:mapCW, vWID[loop]);
	}
	else
	{
		:k.Generate(tmpVec, 1);
		VectorPush(v, tmpVec[0]);
	}
	
	
	Inc(loop);
}
AppLog("[Flow_out]: ",v,vWID);

CVUnlock(:CVHTTP);

switch (GetCount(Parameter))
{
case 5
	HTTP_FlowOut(carrier, v, size, k, r, flowInfo, ppid);
	break;

case 4
	HTTP_FlowOut(carrier, v, size, k, r, flowInfo);
	break;
}

EndScript

EndScript

Script StartLoop
(host)
:host.MessageLoop();
EndScript


Script MES_FlowIn 
(carrier, vwid, size, k, ppid)
switch (GetCount(Parameter))
{
case 5
	:MES.TX_FlowIn(carrier, vwid, size, k, ppid);
	break;
	
case 4
	:MES.TX_FlowIn(carrier, vwid, size, k);
	break;
}

EndScript

Script GetUpStreamJSON
(p, j, event, data)

//p is the CUpStream object
//j is the object to prepare the JSONParse
//event is the transaction name
//data is the optional data template name

GetJSON("TX_JSON_Template", j);
j["event"] = event;
j["event_time"] = ToAikoTime(FT2QST(CurrentFT()));

j["metadata"]["msg_seq_ns"] = p.ns;
j["metadata"]["msg_seq"] = Inc(p.seq);

if (GetCount(Parameter) == 4)
{
	return GetJSON(data, j["data"]);
}

return 0;

EndScript

Script MES_FlowInEx
(m, rfid, vwid, size, k, ppid)

String s, r;
String out;
U4 ret;
Object J, J2;

String event = "flow_in";
String header;
String outRes, outHeader, outBody;
Reference pData, pWafers;
Reference pPath;
U4 loop;
Vector tmpv;

GetUpStreamJSON(:m, J, event, "FlowInData");

RefSet(pData, J["data"]);
pData["cass_id"] = rfid;
StrAssign(pData["eqp_blk"], :EAP["EqpBlockID"]);
pData["lane"] = :k.K;
pData["control_mode"] = :k.Mode;
pData["ppid"] = ppid;
pData["capability"] = :k.Capability;

if (IFind(pPath, :m.mPath, event) == 0)
{
	AppLog("[Error] Path not defined for ", event);
	return -1;
}

if (HTTPClientHelperEx(:m.httpclient, pPath, header, J, outRes, outHeader, outBody))
{
	AppLog("[Error] MES_FlowInEx Failed");
	return -1;
}

if (JSONParse(J2, outBody))
{
	AppLog("[Error] Bad JSON response at ", ScriptName(), " ", outBody);
	return -1;
}

if (IFindEx(pData, J2, "data") == 0 || GetActualType(pData) == "EOF" || IFindEx(pWafers, pData, "wafers") == 0 || GetActualType(pWafers) == "EOF")
{
	loop = 0;
	while (loop < size)
	{
		:k.Generate(tmpv, 1);
		MapAdd(:mapCW, vwid[loop], tmpv[0]);
		AppLog("[FlowIn] Generated ", vwid[loop], "-", tmpv[0]);
		Inc(loop);
	}
}
else
{
	
	if (GetCount(J2["data"]["wafers"]) == size)
	{
		loop = 0;
		while (loop < size)
		{
			MapAdd(:mapCW, vwid[loop], J2["data"]["wafers"][loop]);
			AppLog("[FlowIn] Added ", vwid[loop], "-", J2["data"]["wafers"][loop]);
			Inc(loop);
		}
	}
	else
	{
		if (GetCount(J2["data"]["wafers"]) < size)
		{
			loop = 0;
			while (loop < GetCount(J2["data"]["wafers"]))
			{
				MapAdd(:mapCW, vwid[loop], J2["data"]["wafers"][loop]);
				AppLog("[FlowIn] Added ", vwid[loop], "-", J2["data"]["wafers"][loop]);
				Inc(loop);
			}
			
			while (loop < size)
			{
				:k.Generate(tmpv, 1);
				MapAdd(:mapCW, vwid[loop], tmpv[0]);
				AppLog("[FlowIn] Generated ", vwid[loop], "-", tmpv[0]);
				Inc(loop);
			}
		}
	}
}

HandleFlowInReply(J2, vwid);
return 0;

EndScript

Script MES_FlowInCompleteEX
(m, carrier, qty, last, undockmode, k, ppid)   //rfid,75,0,0,wgen,PPIDActual

U4 ret;
Object J, J2;
String event = "flow_in_complete";
String header,outRes,outHeader,outBody;
Reference pData,pPath;

GetUpStreamJSON(:m, J, event, "FlowInCompleteData");

RefSet(pData, J["data"]);
pData["cass_id"] = carrier;
StrAssign(pData["eqp_blk"], :EAP["EqpBlockID"], StrSubR(k, 0, 1));
pData["lane"] = :k.K;
pData["control_mode"] = :k.Mode;
pData["ppid"] = ppid;
pData["capability"] = :k.Capability;
pData["load_qty"] = qty;
pData["last_slot"] = last;
pData["undock_mode"] = undockmode;



if (IFind(pPath, :m.mPath, event) == 0)
{
	AppLog("[Error] Path not defined for ", event);
	return -1;
}

if (HTTPClientHelperEx(:m.httpclient, pPath, header, J, outRes, outHeader, outBody))
{
	AppLog("[Error] MES_FlowInCompleteEx Failed");
	return -1;
}

if (JSONParse(J2, outBody))
{
	AppLog("[Error] Bad JSON response at ", ScriptName(), " ", outBody);
	return -1;
}


EndScript


Resource FlowInCompleteData
{
	"cass_id": "",
    "eqp_blk": "",
	"lane":"",
	"control_mode":"",
	"ppid":{},
    "load_qty": 0,
    "last_slot" : 0,
    "undock_mode": "",
	"capability":""
}
EndResource

Script MES_FlowInComplete
(carrier, qty, last, undockmode, k, ppid)

switch (GetCount(Parameter))
{
case 6
	:MES.TX_FlowInComplete(carrier, qty, last, undockmode, k, ppid);
	break;
	
case 5
	:MES.TX_FlowInComplete(carrier, qty, last, undockmode, k);
	break;
}
	
EndScript

Script MES_FlowOut
(carrier, vwid, size, k, ppid)
switch (GetCount(Parameter))
{
case 4
	:MES.TX_FlowOut(carrier, vwid, size, k);
	break;
	
case 5
	:MES.TX_FlowOut(carrier, vwid, size, k, ppid);
	break;
}

EndScript


Script MES_FlowOutEx
(mes, carrier, vWID, size, k, ppid)

//vWID should be type Vector

Vector v, tmpVec;
Reference p, pData, pPath;
U8 u8,tickKey;
U4 ret,count = 0, loop = 0;
String event = "flow_out";
Object j;
String s, tmp, strTick;
String key;

PString pStrTick;

Reference pMESKey, pMESInfo, pLocal;
Map lMap;		//local Map to count the most number of wafer, key is FT
U0 index;
String iKey, tmpkey;
String header, outRes, outHeader, outBody;

IMap flowInfo;
AppLog("FlowOut for ", carrier, ", quantity is ", size);

//routine to implement algo such that the FlowOut is using the MapMESInfo of the most wafers in that cassette
foreach(p, vWID, index)
{
	if (IFind(pMESKey, :MapMESKey, p) && IFind(pMESInfo, :MapMESInfo, pMESKey))		//need to computate
	{
		if (IFind(pLocal, lMap, pMESKey))
		{
			Inc(pLocal);
		}
		else
		{
			MapAdd(lMap, pMESKey, 1);
		}
	}
}

foreach (pLocal, lMap, iKey)
{
	if (count == 0 || pLocal > count)
	{
		key = iKey;
		count = pLocal;
	}
	AppLog("Count for key ", key, " is ", pLocal);
}

if (StrLen(key))
{
	AppLog("Key ", key, " is selected for FlowOut info");
	flowInfo = :MapMESInfo[key];
	AppLog(flowInfo);
}
else			//take the last FlowIn info
{
	AppLog("No FlowOut info available among the out wafers in casssete.  Will use current MES info");
	IEnd(pMESInfo, :MapMESInfo);
	if (IDec(pMESInfo))
	{
		flowInfo = pMESInfo;
		AppLog(flowInfo);
	}
	else
	{
		AppLog("No flowInfo is assigned");
	}
}

while (loop < size)			//cycle through
{
	if (MapCheck(:mapCW, vWID[loop]) && StrLen(:mapCW[vWID[loop]]))
	{
		VectorPush(v, :mapCW[vWID[loop]]);	
		MapRemove(:mapCW, vWID[loop]);
	}
	else
	{
		:k.Generate(tmpVec, 1);
		VectorPush(v, tmpVec[0]);
	}
	
	
	Inc(loop);
}

AppLog("[FlowOut]: ", carrier, "\x0A", v, vWID);

GetUpStreamJSON(:mes, j, event, "FlowOutData");

RefSet(pData, j["data"]);
pData["cass_id"] = carrier;
pData["eqp_blk"] = ifexp(IMapCheck(:EAP, "EqpBlockID"))(:EAP["EqpBlockID"])(:EAP["ToolID"]);

if(IMapCheck(flowInfo,"data") && GetActualType(flowInfo["data"]) <> "EOF")
{
	if(IMapCheck(flowInfo["data"],"wo_id"))
	{
		pData["wo_id"] = flowInfo["data"]["wo_id"];		//:k.WorkOrder;
	}
	pData["wo_dept_id"] = flowInfo["data"]["wo_dept_id"]; //deptid
	pData["prod_code"]	= flowInfo["data"]["prod_code"]; //deptid
	pData["tech"]		= flowInfo["data"]["tech"]; //tech
	pData["prod_id"]	= flowInfo["data"]["prod_id"];//id
	pData["prod_name"]	= flowInfo["data"]["prod_name"];//name
	pData["flow_id"]	= flowInfo["data"]["flow_id"];
	pData["flow_name"]	= flowInfo["data"]["flow_name"];
}

pData["wafer_qty"] = size;
pData["wafers"] = v;
if(GetCount(Parameter) >= 7)
{
	pData["ppid"] = ppid;
}
pData["capability"] = :k.Capability;

if (IFind(pPath, :mes.mPath, event) == 0)
{
	AppLog("[Error] Path not defined for ", event);
	return -1;
}

if (HTTPClientHelperEx(:mes.httpclient, pPath, header, j, outRes, outHeader, outBody))
{
	AppLog("[Error] ", ScriptName(), " failed");
	return -1;
}

return 0;
	
EndScript


Script MES_EAPStartUp
(k)
:MES.TX_EAPStartUp(k);
EndScript

Resource WaferStart
{
    "cass_id": "",
    "eqp_blk": "",
    "lane": "",
    "control_mode": "",
    "rfid1":"",
    "rfid2":"",
    "ppid": null,
    "wafers": []
}
EndResource

Resource packer_status_change
{
    "lot_id": "",
    "eqp_id": ""
}
EndResource

Script CWaferIDGen
(k)

String K = k;
String Capability;
U4 Serial = 0;
U8 LastTimeStamp = 0;
String Mode;

Capability=:EAP["Type"];

Script SetCapability
(c)
String s = c;
Capability = s;
EndScript

Script SetMode
(m)
switch (m)
{
case 0 
	Mode = "offline";
	break;

case 1
	Mode = "remote";
	break;
	
case 2
	Mode = "delegate";
	break;
	
case 3
	Mode = "local";
	break;
	
default
	Mode = "Unknown";
	break;
}
EndScript

Script Generate
(v, n)

String p;
String tmp;
U4 x;
U4 loop;

if (GetType(v) != "Vector")
{
	AppLog("[Error]", "Parameter 1 in CWaferIDGen must be Vector");
	throw;
}

VectorClear(v);
U8 CurrentTimeStamp = FT2QST(CurrentFT()) / 10000000;
if (CurrentTimeStamp > LastTimeStamp)
{
	Serial = 0;
}

LastTimeStamp = CurrentTimeStamp;
tmp = CurrentTimeStamp;

loop = 0;
while (loop < n)
{
	StrAssign(p, :EAP["ToolID"], K, StrSub(tmp, 3, 1));
	x = (CurrentTimeStamp / 1000000) % 100;		//get the month
	switch (x)
	{
	default
		StrAppend(p, x, StrSub(tmp, 6, 6));
		break;
		
	case 10
		StrAppend(p, "A", StrSub(tmp, 6, 6));
		break;
		
	case 11
		StrAppend(p, "B", StrSub(tmp, 6, 6));
		break;
		
	case 12
		StrAppend(p, "C", StrSub(tmp, 6, 6));
		break;
	}
	
	Serial = Inc(Serial) % 1000;
	if (Serial < 10)
	{
		StrAppend(p, "00", Serial);
	}
	else
	{
		if (Serial < 100)
		{
			StrAppend(p, "0", Serial);
		}
		else
		{
			StrAppend(p, Serial);
		}
	}
	VectorPush(v, p);
	Inc(loop);
}


EndScript

EndScript


Script SendLogToKafka
(wid, lane, bflag, proc_hist, ppid,k, flowio, loop)
SendLogToKafkaBase(wid, lane, bflag, proc_hist, ppid, k, flowio, loop, 1 ,"", "", "");
EndScript

Script SendLogToKafkaEx
(wid, lane, bflag, proc_hist, ppid,k, flowio, loop, outrfid)
SendLogToKafkaBase(wid, lane, bflag, proc_hist, ppid, k, flowio, loop, 1 ,"", "", "",outrfid);
EndScript

Script SendLogToKafkaWithBoat
(wid, lane, bflag, proc_hist, ppid, k, flowio, loop, boatid, zoneid, tubeid)
SendLogToKafkaBase(wid, lane, bflag, proc_hist, ppid, k, flowio, loop, 1, boatid, zoneid, tubeid);
EndScript

Script SendLogToKafkaWithBoatEx
(wid, lane, bflag, proc_hist, ppid, k, flowio, loop, boatid, zoneid, tubeid,outrfid)
SendLogToKafkaBase(wid, lane, bflag, proc_hist, ppid, k, flowio, loop, 1, boatid, zoneid, tubeid,outrfid);
EndScript

Script SendLogToKafkaBase
(wid,lane,bflag,proc_hist,ppid,k,flowio,loop,clearflag,boatid,zoneid,tubeid)
(wid,lane,bflag,proc_hist,ppid,k,flowio,loop,clearflag,boatid,zoneid,tubeid,outrfid)
Reference p, ppp;
Object J;
String s,msg,strTick,currentTime,kafkakey,e;
String msg2;
U4 ret,count;
U8 tickKey;
Vector vtemp;
PString pStrTick;
IMap flowInfo;
CVLock(:CVHTTP);

Reference pMESKey, pMESInfo;

try
{
	if (IFind(pMESKey, :MapMESKey, wid) && IFind(pMESInfo, :MapMESInfo, pMESKey))
	{
		flowInfo = pMESInfo;
		//AppLog("[Debug] Got MESInfo as actual item");
	}
	else
	{
		IEnd(pMESInfo, :MapMESInfo);
		if (IDec(pMESInfo))
		{
			flowInfo = pMESInfo;
			//AppLog("[Debug] Got MESInfo as latest item");
		}
		else
		{
			AppLog("[Error] No MES info is available");
		}
	}

	if (GetCount(flowInfo))
	{
		RefSet(p, flowInfo);
		
		GetResource(ifexp(IFindEx(ppp, :EAP, "Fab5InSlot") && ppp)("FlowWaferEx")("FlowWafer"), s, ret);
		JSONParse(J, s, ret);
		currentTime=ToAikoTime(FT2QST(CurrentFT()));
		J["event_time"]=currentTime;
		J["eqp_block_id"] = ifexp(IMapCheck(:EAP, "eqp_block_id"))(:EAP["eqp_block_id"])(:EAP["EqpBlockID"]);
		J["eqp_id"] = ifexp(IMapCheck(:EAP, "eqp_id"))(:EAP["eqp_id"])(:EAP["EqpBlockID"]);
		J["fab"]=:EAP["Fab"];

		if (MapCheck(:mapCW,wid) && StrLen(:mapCW[wid]))
		{
			J["data"]["wafer_id"]=:mapCW[wid];
		}
		else
		{
			AppLog("[Fatal] ", wid, " not found in mapCW at ", ScriptName());
		}

		if(IMapCheck(p,"data"))
		{
			if(flowio=="10"||flowio=="11")
			{
				if(IMapCheck(p["data"],"comp_id"))
				{
					J["data"]["comp_id"]=p["data"]["comp_id"];
				}
				//J["data"]["comp_id"]=p["data"]["comp_id"];
				J["data"]["cass_id"]=p["data"]["cass_id"];
			}
			if(GetCount(Parameter)==13)
			{
				J["data"]["out_cass_id"]=outrfid;
				J["data"]["out_slot_id"]=loop+1;
				J["data"][ifexp(IFindEx(ppp, :EAP, "Fab5InSlot"))("in_slot_id")("slot_id")] = clearflag;
			}
			if(IMapCheck(p["data"],"wo_id")&&GetActualType(p["data"]) <> "EOF")
			{
				J["data"]["wo_id"]=p["data"]["wo_id"];
			}
			//J["data"]["wo_id"]=p["data"]["wo_id"];
			J["data"]["prod_code"]=p["data"]["prod_code"];
			J["data"]["prod_id"]=p["data"]["prod_id"];
			J["data"]["prod_name"]=p["data"]["prod_name"];
			J["data"]["tech"]=ifexp(GetActualType(p["data"]["tech"]) != "EOF")(p["data"]["tech"])("");
			J["data"]["flow_id"]=p["data"]["flow_id"];
			J["data"]["flow_name"]=p["data"]["flow_name"];
			J["data"]["wo_dept_id"]=p["data"]["wo_dept_id"];
			J["data"]["stage"]=p["data"]["stage"];
			J["data"]["step_name"]=p["data"]["step_name"];
			J["data"]["eqp_blk"]=ifexp(IMapCheck(:EAP, "eqp_blk"))(:EAP["eqp_blk"])(p["data"]["eqp_blk"]);
			J["data"]["lane"]=lane;
			if(IMapCheck(p["data"],"recipe"))
			{
				if(GetActualType(p["data"]["recipe"])=="IMap")
				{
					J["data"]["recipe"]=p["data"]["recipe"];
				}
			}
			
			J["data"]["ppid_plan"]=p["data"]["ppid_plan"];
			J["data"]["ppid_actual"]=ppid;
			if(CompareIMap(J["data"]["ppid_plan"],J["data"]["ppid_actual"]))
			{
				J["data"]["ppid_ap_same"]="Y";
			}
			else
			{
				J["data"]["ppid_ap_same"]="N";
			}
			if(IMapCheck(p["data"],"qtime_actual"))
			{
				J["data"]["qtime_actual"]=p["data"]["qtime_actual"];
			}
			//J["data"]["qtime_actual"]=p["data"]["qtime_actual"];
			J["data"]["flow_io"]=flowio;
			J["data"]["flow_in_time"]=p["event_time"];
			J["data"]["flow_out_time"]=currentTime;
			J["data"]["rec_time"]=currentTime;
			J["data"]["proc_hist"]=proc_hist;
			J["data"]["boat_id"]=boatid;
			J["data"]["zone_id"]=zoneid;
			J["data"]["tube_id"]=tubeid;
		}
		RefRelease(p);
		
		JSONWrite(J, 0, msg, ret);
		if (ret)
		{
			AppLog("[Bug] ", ScriptName(), " Unable to perform JSONWrite");
			AppLog(J);
		}
		else
		{
			PrepareKafka(msg, :EAP["ToolID"], "eap-collection-wafer-cv","F4Kafka");
		}
	}
}
catch (p)
{
	AppLog("Expection caught in SendLogToKafkaBase.  Return to caller");
}
CVUnlock(:CVHTTP);
EndScript

Script SendHalmWaferToKafka
(kafka, wid, halmwid, intime, k, lane, eid, eidOutTime)
String s,msg,currentTime,e;
Vector vtemp;
U4 ret;
Object J;

GetResource("HalmWafer", s, ret);
JSONParse(J, s, ret);
currentTime=ToAikoTime(FT2QST(CurrentFT()));
J["event_time"]=currentTime;
J["eqp_block_id"]=:EAP["EqpBlockID"];
J["eqp_id"]=:EAP["eqp_id"];
J["fab"]=:EAP["Fab"];

if (MapCheck(:mapCW, wid) && StrLen(:mapCW[wid]))
{
	J["data"]["wafer_id"]=:mapCW[wid];
}
else
{
	AppLog("[Fatal] ", wid, " not found in mapCW at ", ScriptName());
}

J["data"]["halm_wafer_id"]=halmwid;
J["data"]["halm_in_time"]=intime;
J["data"]["eid_wafer_id"]=eid;
J["data"]["eid_out_time"]=eidOutTime;

PrepareKafka(J, :EAP["ToolID"], "eap-collection-halm-wafer", kafka);

EndScript

Script CompareIMap
(MapA,MapB)
Reference p;
U0 dummy_index;
String s;

if(GetCount(MapA)!=GetCount(MapB))
{
    return 0;
}

foreach(p, MapA, dummy_index, s)
{
    if(!IMapCheck(MapB,s))
    {
        return 0;
    }

    if(MapA[s]!=MapB[s])
    {
        return 0;
    }
}

return 1;

EndScript


Script PrepareKafka
(msg, key, topic, server)

U0 sb;
Object o;
String s;
U4 ret;

if (GetActualType(msg) == "String")
{
	s = msg;
}
else
{
	JSONWrite(msg, 0, s, ret);
	if (ret)
	{
		AppLog("[Bug] ", ScriptName(), " Unable to perform JSONWrite");
		AppLog(msg);
		return;
	}
}

//check again on validity of s as JSON String

if (JSONParse(o, s))
{
	AppLog("[Bug] Bad JSON in ", ScriptName());
	AppLog(s);
	AppLog(msg);
	AppLog(StackInfo());
	return;
}
	
if (GetCount(Parameter) == 3)
{
	:Kafka.Send(topic, s, sb, key);
}
else
{
	:server.Send(topic, s, sb, key);
}

EndScript

Script InitDC
ConfigDC();
EndScript

Script ConfigDC
U4 ret;
String s,planName;
U0 dummy_index;
Reference p;

StrReadFile(s, "dc.json", ret);
if (ret)
{
    Print("Failed to read 'dc.json'");
    return;
}

    
GCreateEx("DC", "Object");
JSONParse(:DC, s, ret);

if (ret)
{
	AppLog("Failed to parse 'dc.json'");
	return;
}

GCreateEx("GData","IMap");
GCreateEx("OPCEventDCPlans","IMap");
GCreateEx("OPCPoolDCPlans","IMap");
GCreateEx("SECSEventDCPlans","IMap");
GCreateEx("SECSPoolDCPlans","IMap");
GCreateEx("SECSAlarmDCPlans","IMap");

foreach(p,:DC,dummy_index,planName)
{
    if(IMapCheck(p,"Type"))
    {
        switch(p["Type"])
        {
            case "opc_event"
                IMapPush(:OPCEventDCPlans,planName,planName);
            break;
            case "opc_pool"
                IMapPush(:OPCPoolDCPlans,planName,planName);
            break;
            case "secs_event"
                IMapPush(:SECSEventDCPlans,planName,planName);
            break;
            case "secs_pool"
                IMapPush(:SECSPoolDCPlans,planName,planName);
            break;
            case "secs_alarm"
                IMapPush(:SECSAlarmDCPlans,planName,planName);
            break;
        }
    }

    InitGData(planName);
}

DCHandle_SECSPool();
DCHandle_OPCPool();
	
EndScript

Script InitGData
(plan)
String s,name,keyStr;
U0 dummy_index;
Vector vKey;
Reference p;

    if(IMapCheck(:DC[plan],"ExtraData"))
    {
        foreach(p,:DC[plan]["ExtraData"],dummy_index,name)
        {
            keyStr=p;
            StrTokEx(keyStr,0,0,",",vKey);

            if(GetCount(vKey)>1)
            {
                if(!IMapCheck(:GData,vKey[0]))
                {
                    IMapPushEx(:GData,vKey[0],vKey[1]);
                }
            }
            else
            {
                if(!IMapCheck(:GData,vKey[0]))
                {
                    IMapPushEx(:GData,vKey[0],"String");
                }
            }    
        }
    }

EndScript

Script DCHandle_OPCPool
    Reference p;
    String s;
    U0 i;
    
	if(MapCheck(Global,"OPCPoolDCPlans"))
	{
		foreach(p,:OPCPoolDCPlans,i,s)
        {
            if(IMapCheck(:DC[s],"OPCHost"))
            {
                if(IMapCheck(:DC[s],"Interval"))
                {
                    PCall("CollectOPCTask",s);
                }
                else
                {
                    AppLog("[DC Error] Plan ",s,"did not config OPCHost");
                }
            }
            else
            {
                AppLog("[DC Error] Plan ",s,"did not config OPCHost");
            }
        
        }
	}

        
EndScript

Script DCHandle_OPC
(alias,value,host)
(alias,value,host,extraData)
Reference p;
String s;
U0 i;

if(MapCheck(Global,"OPCEventDCPlans"))
{
	foreach(p,:OPCEventDCPlans,i,s)
    {
        if(IMapCheck(:DC[s],"eventid"))
        {
            if(:DC[s]["eventid"]==alias)
            {
              
                if(GetCount(Parameter)==4)
                {
                    QueryOPCData(s,host,extraData);
                }
                else
                {
                    QueryOPCData(s,host);
                }
               

            }

        }

    }
}
    
EndScript

Script QueryOPCData
(plan,Host,extraData)

Vector vName,vNode,vValue;
Reference p;
String s;
U0 i;
U2 ret;

if(IMapCheck(:DC[plan],"data"))
{
    foreach(p,:DC[plan]["data"],i,s)
    {
        VectorPush(vName,s);
        VectorPush(vNode,p);
    }

    :Host.ReadMulti(vNode, vValue,ret);

    if(ret)
    {
        AppLog("Plan ",plan," read node fail",vNode);
        return;
    }

    if(GetCount(Parameter)==3)
    {
        ComposeKafka(plan,vName,vValue,extraData);
    }
    else
    {
		ComposeKafka(plan, vName, vValue);
	}
    
}
else
{
    AppLog("Plan ",plan," has no data config");
}

EndScript

Script DCHandle_SECSAlarm
(engine,msg)
(engine,msg,extraData)
Reference p;
String s;
U0 i;

if(MapCheck(Global,"SECSAlarmDCPlans"))
{
	foreach(p,:SECSAlarmDCPlans,i,s)
    {
        if(IMapCheck(:DC[s],"Tool"))
        {
            if(:DC[s]["Tool"]==engine)
            {
                if(GetCount(Parameter)==3)
                {
                    GetSecsAlarmData(msg,s,extraData);
                }
                else
                {
                    GetSecsAlarmData(msg,s);
                }
            }

        }

    }
}
EndScript

Script GetSecsAlarmData
(msg,plan)
(msg,plan,extraData)
Vector vName,vNode,vValue;
Reference p;
String s;
U0 i;
U2 ret;
U1 u1;

if(IMapCheck(:DC[plan],"data"))
{

	/*
    foreach(p,:DC[plan]["data"],i,s)
    {
        VectorPush(vName,s);
        VectorPush(vValue,GetValueByTrace(msg,:DC[plan]["data"][s]));
    }
	*/
	
	BinaryAssign(u1, msg[0][0]);
	VectorPush(vName, "AlarmState");
	VectorPush(vValue, ifexp(u1 & 0x80)("On")("Off"));

	VectorPush(vName, "AlarmCode");
	VectorPush(vValue,GetValueByTrace(msg, :DC[plan]["data"][0]));

	VectorPush(vName, "AlarmText");
	VectorPush(vValue,GetValueByTrace(msg, :DC[plan]["data"][1]));

 
    if(GetCount(Parameter)==3)
    {
        ComposeKafka(plan,vName,vValue,extraData);
    }
    else
    {
        ComposeKafka(plan,vName,vValue);
    }
    
}
else
{
    AppLog("Plan ",plan," has no data config");
}
EndScript

Script DCHandle_SECSEvent
(engine,msg)
(engine,msg,extraData)
Reference p;
String s;
U0 i;
    
if(MapCheck(Global,"SECSEventDCPlans"))
{
	foreach(p,:SECSEventDCPlans,i,s)
	{
		if(IMapCheck(:DC[s],"Tool"))
		{
			if(:DC[s]["Tool"]==engine)
			{
				if(IMapCheck(:DC[s],"eventid"))
				{
					if(:DC[s]["eventid"]== msg[1][0])
					{
						if(GetCount(Parameter)==3)
						{
							GetSecsEventData(msg,s,extraData);
						}
						else
						{
							GetSecsEventData(msg,s);
						}
					}
				}
			}
	
		}
	
	}
}

EndScript

Script GetSecsEventData
(msg,plan)
(msg,plan,extraData)
Vector vName,vNode,vValue;
Reference p;
String s;
U0 i;
U2 ret;

if(IMapCheck(:DC[plan],"data"))
{
    foreach(p,:DC[plan]["data"],i,s)
    {
        VectorPush(vName,s);
        VectorPush(vValue,GetValueByTrace(msg,p));
    }
	
    if(GetCount(Parameter)==3)
    {
        ComposeKafka(plan,vName,vValue,extraData);
    }
    else
    {
        ComposeKafka(plan,vName,vValue);
    }
    
}
else
{
    AppLog("Plan ",plan," has no data config");
}
EndScript

Script GetValueByTrace
(msg, trace)
U4 count,index;
Vector vTemp;
String res;
U4 ret;
U4 n;
String s, type;
U1 x;

if(GetCount(trace)>0)
{
    
    index=trace[0];
    vTemp=trace;
    VectorRemove(vTemp,0);
   
    return GetValueByTrace(msg[index],vTemp);
}
else
{
	type = GetActualType(msg);
	if(type=="B")
	{
		BinaryAssign(x,msg[0]);
		return x;
	}
	
    if(type=="A")
    {
        res=msg[];
        StrReplace(res,"\"","\\\"");
        return res;
    }
    else
    {
        return msg[0];
    }
}
EndScript

Script DCHandle_SECSPool
Reference p;
String s;
U0 i;
  if(MapCheck(Global,"SECSPoolDCPlans"))  
  {
	foreach(p,:SECSPoolDCPlans,i,s)
    {
        if(IMapCheck(:DC[s],"Tool"))
        {
            if(IMapCheck(:DC[s],"Interval"))
            {
                PCall("CollectS1F3Task",s);
            }
            else
            {
                AppLog("[DC Error] Plan ",s,"did not config Tool");
            }
        }
        else
        {
            AppLog("[DC Error] Plan ",s,"did not config Tool");
        }
    
    }
  }
    
EndScript

Script CollectS1F3Task
(plan)
String Tool;
Vector vName,vValue;
U4 count,loop;
I4 ret;
U8 cTime,interval,x,t,hour,minute,second;
String strVID,svidType;
PrintLn("CollectS1F3Task started for ", plan);
SecsMessage@Secs m, rmsg;
ret = 0;

    Tool=:DC[plan]["Tool"];
    if(IMapCheck(:DC[plan],"data"))
    {
        loop = 0;
        count = GetCount(:DC[plan]["data"]);

        svidType="U4"; //default type U4
        if(IMapCheck(:DC[plan],"SVIDType"))
        {
            svidType=:DC[plan]["SVIDType"];
        }


        SetSFW@Secs(m, 1, 3, 1);
        Set@Secs(m, "List", count, 0, 0, ret);
        while(loop < count)
        {
            strVID = IMapGetKey(:DC[plan]["data"],loop);
            VectorPush(vName,strVID);
            Set@Secs(m[loop], svidType, 1, 0, :DC[plan]["data"][strVID], ret);
            Inc(loop);
        }
    }

  
    if(IMapCheck(:DC[plan],"Start"))
    {
        if(:DC[plan]["Start"]=="hourly")
        {
            while(1)
            {
                CurrentFT(x);
                FT2QST(x,t);//
                t = (t/100000)%1000000;
                hour = t/10000;
                minute = (t/100)%100;
                second = t%100;
                if(minute == 0 && second == 0)
                {
                    wbreak;
                }
                Sleep(1000);
            }
        }
        if(:DC[plan]["Start"]=="halfhourly")
        {
            while(1)
            {
                CurrentFT(x);
                FT2QST(x,t);//
                t = (t/100000)%1000000;
                hour = t/10000;
                minute = (t/100)%100;
                second = t%100;
                if((minute == 0 || minute == 30) && second == 0)
                {
                    wbreak;
                }
                Sleep(1000);
            }
        }
        
    }

    
    
    
    while(1)
    {
        cTime=CurrentFT();
        if(IMapCheck(:DC[plan],"data"))
        {
		//ret = Send_RequestSync("Tool", m, rmsg);
            //ret = SendAndWaitEx@@Secs(:Tool, m, rmsg, QS_EID());
			
            cTime=CurrentFT() - cTime;

            if (Send_RequestSync(:DC[plan]["Tool"], m, rmsg) || (GetFunctionEx@@Secs(rmsg) == 0))
            {
				PrintLn("SendAndWaitEx@@Secs failed, ", ret);
            }
            else
            {
                loop=0;
                VectorClear(vValue);
                while(loop<count)
                {
                    if (GetCount(rmsg[loop]))
                    {
                        if(GetActualType(rmsg[loop])=="A")
                        {
                            VectorPush(vValue,rmsg[loop][]);
                        }
                        else
                        {
                            VectorPush(vValue,rmsg[loop][0]);
                        }
                    }
                    else
                    {
                        VectorPush(vValue,"");
                    }
                
                    Inc(loop);
                }

                ComposeKafka(plan,vName,vValue);
            }
        }
        else
        {
            cTime=CurrentFT()-cTime;
            ComposeKafka(plan,vName,vValue);
        }
       

        if(:DC[plan]["Interval"] > cTime/10000)
        {
            Sleep(:DC[plan]["Interval"]-cTime/10000);
        }
        
    }

EndScript

Script CollectOPCTask
(plan)
String Host,strVID;
Vector vName,vNode,vValue;
U4 count,loop,ret;
U8 cTime,x,t,hour,minute,second;
ret = 0;
    Host=:DC[plan]["OPCHost"];
    if(IMapCheck(:DC[plan],"data"))
    {
        loop = 0;
        count = GetCount(:DC[plan]["data"]);
    

        while(loop < count)
        {
            strVID = IMapGetKey(:DC[plan]["data"],loop);
            VectorPush(vName,strVID);
            VectorPush(vNode,:DC[plan]["data"][strVID]);
            Inc(loop);
        }
    }

  
        if(IMapCheck(:DC[plan],"Start"))
        {
            if(:DC[plan]["Start"]=="hourly")
            {
                while(1)
                {
                    CurrentFT(x);
                    FT2QST(x,t);//
                    t = (t/100000)%1000000;
                    hour = t/10000;
                    minute = (t/100)%100;
                    second = t%100;
                    if(minute == 0 && second == 0)
                    {
                        wbreak;
                    }
                    Sleep(1000);
                }
            }
            if(:DC[plan]["Start"]=="halfhourly")
            {
                while(1)
                {
                    CurrentFT(x);
                    FT2QST(x,t);//
                    t = (t/100000)%1000000;
                    hour = t/10000;
                    minute = (t/100)%100;
                    second = t%100;
                    if((minute == 0 || minute == 30) && second == 0)
                    {
                        wbreak;
                    }
                    Sleep(1000);
                }
            }
            
        }

    while(1)
    {
        cTime=CurrentFT();
        if(IMapCheck(:DC[plan],"data"))
        {
            VectorClear(vValue);
             Global[Host].QReadMulti(vNode, vValue, ret);

            cTime=CurrentFT()-cTime;

            if(ret)
            {
            PrintLn("ColoectFromOPC failed");
            }
            else
            {
                ComposeKafka(plan,vName,vValue);
            }
        }
        else
        {
            cTime=CurrentFT()-cTime;
            ComposeKafka(plan,vName,vValue);
        }
       

        if(:DC[plan]["Interval"]>cTime/10000)
        {
            Sleep(:DC[plan]["Interval"]-cTime/10000);
        }
    }
EndScript

Script ComposeKafka
(plan,vName,vValue)
(plan,vName,vValue,extraData)
IMap J;
U4 loop,count,ret;
Vector vKey;
String msg,eqpid,topic,s,es,storeKey,dataName,MapKey;
Reference p,ex;
U0 i,j;

loop=0;

if(!IMapCheck(:DC[plan],"Topic"))
{
    PrintLn("[DC Error] Plan ",plan," Loss Topic config");
    return;
}

if(!IMapCheck(:DC[plan],"MsgHeader"))
{
    PrintLn("[DC Error] Plan ",plan," Loss MsgHeader config");
    return;
}

if(!IMapCheck(:DC[plan]["MsgHeader"],"eqp_id"))
{
    PrintLn("[DC Error] Plan ",plan," Loss eqpid config");
    return;
}

topic=:DC[plan]["Topic"];
eqpid=:DC[plan]["MsgHeader"]["eqp_id"];


J=:DC[plan]["MsgHeader"];

J["event_time"]=StrTime("%Z");
IMapPushEx(J,"data","IMap");

count=GetCount(vName);
while(loop<count)
{
	MapKey = vValue[loop];
    if(IMapCheck(:DC[plan],"DataMap"))
    {
        if(IMapCheck(:DC[plan]["DataMap"],vName[loop]))
        {
            if(IMapCheck(:DC[plan]["DataMap"][vName[loop]],MapKey))
            {
				dataName = vName[loop];

                IMapPush(J["data"],vName[loop],:DC[plan]["DataMap"][vName[loop]][MapKey]);
               
                if(IMapCheck(:DC[plan],"StoreData"))
                {
                    if(IMapCheck(:DC[plan]["StoreData"],vName[loop]))
                    {
                        if(IMapCheck(:GData,:DC[plan]["StoreData"][vName[loop]]))
                        {
                             storeKey=:DC[plan]["StoreData"][vName[loop]];
                            :GData[storeKey]=:DC[plan]["DataMap"][vName[loop]][MapKey];
                        }
                        else
                        {
                            IMapPush(:GData,:DC[plan]["StoreData"][vName[loop]],:DC[plan]["DataMap"][vName[loop]][MapKey]);
                        }

                    }
                }
            }
            else
            {
                IMapPush(J["data"],vName[loop], vValue[loop]);

                if(IMapCheck(:DC[plan],"StoreData"))
                {
                    if(IMapCheck(:DC[plan]["StoreData"],vName[loop]))
                    {
                        if(IMapCheck(:GData,:DC[plan]["StoreData"][vName[loop]]))
                        {
                            storeKey=:DC[plan]["StoreData"][vName[loop]];
                            :GData[storeKey]= vValue[loop];
                        }
                        else
                        {
                            IMapPush(:GData,:DC[plan]["StoreData"][vName[loop]], vValue[loop]);
                        }

                    }
                }
            }
        }
        else
        {
            IMapPush(J["data"],vName[loop], vValue[loop]);
            if(IMapCheck(:DC[plan],"StoreData"))
            {
                if(IMapCheck(:DC[plan]["StoreData"],vName[loop]))
                {
                    if(IMapCheck(:GData,:DC[plan]["StoreData"][vName[loop]]))
                    {
                        storeKey=:DC[plan]["StoreData"][vName[loop]];
                        :GData[storeKey]= vValue[loop];
                    }
                    else
                    {
                        IMapPush(:GData,:DC[plan]["StoreData"][vName[loop]], vValue[loop]);
                    }

                }
            }
        }
    }
    else
    {
        IMapPush(J["data"],vName[loop],vValue[loop]);
        if(IMapCheck(:DC[plan],"StoreData"))
        {
            if(IMapCheck(:DC[plan]["StoreData"],vName[loop]))
            {
                if(IMapCheck(:GData,:DC[plan]["StoreData"][vName[loop]]))
                {
                    storeKey=:DC[plan]["StoreData"][vName[loop]];
                    :GData[storeKey]=vValue[loop];
                }
                else
                {
                    IMapPush(:GData,:DC[plan]["StoreData"][vName[loop]],vValue[loop]);
                }

            }
        }
    }

    
    Inc(loop);
}

if(GetCount(Parameter)==4)
{
    if(GetType(extraData)=="IMap")
    {
        foreach(p,extraData,i,s)
        {
            IMapPush(J["data"],s,p);
        }
    }
}


if(IMapCheck(:DC[plan],"ExtraData"))
{
    foreach(ex,:DC[plan]["ExtraData"],j,es)
    {
        MapKey=es;
        dataName=ex;
        StrTokEx(dataName,0,0,",",vKey);
        if(IMapCheck(:GData,vKey[0]))
        {
            IMapPush(J["data"],MapKey,:GData[vKey[0]]);
        }
        else
        {
            IMapPush(J["data"],MapKey,"");
        }
        
    }
}

if(IMapCheck(:DC[plan],"FinalData"))
{
	foreach(p,:DC[plan]["FinalData"],j,es)
	{
		MapKey=es;
		IMapPush(J["data"],MapKey,:DC[plan]["FinalData"][es]);
	}
}

if(IMapCheck(:DC[plan],"F4Kafka"))
{
	PrepareKafka(J, eqpid, topic,"F4Kafka");
}
else
{
	PrepareKafka(J, eqpid, topic);
}
EndScript

Script FTimer
(fun, interval)

U8 t0 = CurrentFT();
U8 t1, elapse;
U8 expire = t0 + interval * 10000U8;

Vector v = Parameter;
VectorRemove(v, 0);
VectorRemove(v, 0);

while (1)
{
	FCallEx(fun, v);
	t1 = CurrentFT();
	
	if (expire > t1)
	{
		Sleep((expire - t1) / 10000);
		t0 = expire;
		expire = expire + interval * 10000U8;
	}
	else
	{
		t0 = CurrentFT();
		expire = t0 + interval * 10000U8;
	}
}
EndScript

Script CreateFTimer
PCallEx("FTimer", Parameter);
EndScript

Resource FlowOutData
{   
	"cass_id":  "",
	"eqp_blk": "",
	"wo_id": "",
	"wo_dept_id": "",
	"prod_code": "",
	"tech": null,
	"prod_id": "",
	"prod_name": "",
	"flow_id": "",
	"flow_name": "",
	"wafer_qty": 0,
	"wafers":  [],
	"ppid":{},
	"capability":""
}
EndResource

Resource FlowOutEx
{   
    "event": "flow_out",
    "event_time": "",
    "data": {   
		"cass_id":  "",
        "eqp_blk": "",
        "wo_id": "",
        "wo_dept_id": "",
        "prod_code": "",
        "tech": null,
        "prod_id": "",
        "prod_name": "",
        "flow_id": "",
        "flow_name": "",
        "wafer_qty": 0,
        "wafers":  [],
		"ppid":{},
		"capability":""
    },
    "metadata": {
        "msg_seq_ns": "",
        "msg_seq": 0
    }
}

EndResource


Resource FlowInComplete
{   
    "event": "flow_in_complete",
    "event_time": "",
    "data": {   
        "cass_id": "",
        "eqp_blk": "",
		"lane":"",
		"control_mode":"",
		"ppid":{},
        "load_qty": 0,
        "last_slot" : 0,
        "undock_mode": "",
		"capability":""
    },
    "metadata": {
        "msg_seq_ns": "",
        "msg_seq": 0
    }
}
EndResource

Resource FlowInData
{
	"cass_id": "",
	"eqp_blk": "",
	"lane":"",
	"control_mode": "",
	"ppid":null,
	"capability":""
}
EndResource

Resource FlowInEx
{
	"event": "flow_in",
	"event_time": "",
	"data":
	{
		"cass_id": "",
		"eqp_blk": "",
		"lane":"",
		"control_mode": "",
		"ppid":null,
		"capability":""
	},
	"metadata":
	{
		"msg_seq_ns": "",
		"msg_seq" : 0
	}
}
EndResource

Resource EAPStartUp
{   
    "event": "eap_startup",
    "event_time": "20210528 15:34:26.123456",
    "data": {
       "url": "http://172.16.98.218:123/api",
       "comm_type": "http",
       "eqp_blk": "",
       "control_mode": "offline",
	   "service":["label print"],
       "ppid": {}
    },
    "metadata": {
        "msg_seq_ns": "",
        "msg_seq": 0
    }
}
EndResource

Resource FlowWafer
{
	"target": "",
	"event_time": "",
	"eqp_block_id": "",
	"eqp_id": "",
	"fab": "Fab4",
	"meta_data": {
        "date_type": "test"
    },
	"data":{
		"wafer_id":"",
		"comp_id":"",
		"cass_id":"",
		"slot_id":"",
		"out_cass_id":"",
		"out_slot_id":"",
		"wo_id":"",
		"prod_code":"",
		"prod_id":"",
		"prod_name":"",
		"tech":"",
		"flow_id":"",
		"flow_name":"",
		"wo_dept_id":"",
		"stage":"",
		"step_name":"",
		"eqp_blk":"",
		"lane":"",
		"recipe":{},
		"ppid_plan":null,
		"ppid_actual":null,
		"ppid_ap_same":"",
		"qtime_actual":null,
		"flow_io":"",
		"flow_in_time":"",
		"flow_out_time":"",
		"rec_time":"",
		"proc_hist":[],
		"boat_id":"",
        "zone_id":"",
		"tube_id":"",
		"eid_box_id":""
	}
	
}
EndResource

Resource FlowWaferEx
{
	"target": "",
	"event_time": "",
	"eqp_block_id": "",
	"eqp_id": "",
	"fab": "Fab4",
	"meta_data": {
        "date_type": "test"
    },
	"data":{
		"wafer_id":"",
		"comp_id":"",
		"cass_id":"",
		"in_slot_id":"",
		"out_cass_id":"",
		"out_slot_id":"",
		"wo_id":"",
		"prod_code":"",
		"prod_id":"",
		"prod_name":"",
		"tech":"",
		"flow_id":"",
		"flow_name":"",
		"wo_dept_id":"",
		"stage":"",
		"step_name":"",
		"eqp_blk":"",
		"lane":"",
		"recipe":{},
		"ppid_plan":null,
		"ppid_actual":null,
		"ppid_ap_same":"",
		"qtime_actual":null,
		"flow_io":"",
		"flow_in_time":"",
		"flow_out_time":"",
		"rec_time":"",
		"proc_hist":[],
		"boat_id":"",
        "zone_id":"",
		"tube_id":"",
		"eid_box_id":""
	}
	
}
EndResource

Resource HalmWafer
{
	"target": "",
	"event_time": "2021-11-05 11:21:45.23766",
	"eqp_block_id": "",
	"eqp_id": "",
	"fab": "",
	"meta_data": {
			"date_type": "test"
	},
	"data": {
			"wafer_id":"",
			"halm_wafer_id":"",
			"halm_in_time":"",
			"eid_wafer_id":"",
			"eid_out_time":""
	}
}
EndResource

Script Init_NS
(ns)
ns = ToAikoTime(FT2QST(CurrentFT()));
EndScript

Script MES_Helper
(content, event, data)

U4 ret;
Object o;
String d;

static String ns;
static U8 sequence = 0;

if (StrLen(ns) == 0)
{
	Init_NS(ns);
}

if (GetJSON("TX_JSON_Template", o))
{
	return -1;
}

o[0] = event;
o[1] = ToAikoTime(FT2QST(CurrentFT()));
o[2] = data;
o[3][0] = ns;
o[3][1] = Inc(sequence);

JSONWrite(o, 0, content, ret);
return ret;

EndScript

Script MES_EXECUTE
(event, data, rdata)

curl@curl x;
String s, t , t1;
Object info;
I4 ret;

switch (event)
{
case "screen_placed"
case "screen_removed"
case "screen_print_count"

	StrAssign(t, "http://", :EAP["DomainName"], "/mes/api/durable/", event);		//t is now the MES API
	
	StrAssign(t1, "http://", :EAP["DomainName2"], "/api/durable/", event);		//t is now the MES API For Test Server
	break;
		
	
default
	AppLog("Error in ", ScriptName(), ", unknown event ", event);
	return -1;
}

if (MES_Helper(s, event, data))
{
	AppLog(ScriptName(), " Error");
	return -1;
}

AppLog("Sending to MES ", s);
AppLog("Send address = ", t);
SetOption@@curl(x, "CURLOPT_URL", t);
SetOption@@curl(x, "CURLOPT_COPYPOSTFIELDS", s);

if (AddHeader@@curl(x, "Content-Type: application/json"))
{
	PrintLn("Failed to add header Content-Type: application/json");
	return;
}

if (Perform@@curl(x, t))
{
	AppLog(ScriptName(), "[Error] ", t);
	return -1;
}

//Curl operation is succesful, now can examine the return data

if (GetReplyBody@@curl(x, t) == 0)
{
	AppLog("MES returned ", t);
	rdata = t;
	U4 res = 0;
}

/////////////////////////////////////////////send to test server

AppLog("Sending to MES ", s);
AppLog("Send address = ", t1);
SetOption@@curl(x, "CURLOPT_URL", t1);
if (Perform@@curl(x, t1))
{
	AppLog(ScriptName(), "[Error] ", t1);
	return -1;
}

if (GetReplyBody@@curl(x, t1) == 0)
{
	AppLog("MES returned ", t1);
	rdata = t;
	res = 0;
}

if(res == 0)
{
	return 0;
}
else
{
	return -1;
}

EndScript


Script MES_screen_placed
(eqp_blk, location, id, batch, code, print_count, rstr)

U4 ret;
Object o;
String d;

if (GetJSON("screen", o))
{
	return -1;
}

o[0] = eqp_blk;
o[1] = location;
o[2] = id;
o[3] = batch;
o[4] = code;
o[5] = print_count;

return MES_EXECUTE("screen_placed", o, rstr);

EndScript

Script MES_screen_removed
(eqp_blk, location, id, batch, code, print_count, rstr)

U4 ret;
Object o;
String d;

if (GetJSON("screen", o))
{
	return -1;
}

o[0] = eqp_blk;
o[1] = location;
o[2] = id;
o[3] = batch;
o[4] = code;
o[5] = print_count;

return MES_EXECUTE("screen_removed", o, rstr);
EndScript

Script MES_screen_print_count
(eqp_blk,print_count,print_count_max,reset_flag,rstr)

U4 ret;
Object o;
String d;
U0 i = 0;
U0 ii = 0;
if (GetJSON("screen_wafer", o))
{
	return -1;
}
o[0] = eqp_blk;

while(i <4)
{
ii = i +1;
o[1][ii][1] = print_count[i];
o[1][ii][2] = print_count_max[i];
o[1][ii][3] = reset_flag[i];
Inc(i);
}

return MES_EXECUTE("screen_print_count", o, rstr);
EndScript



Resource screen
{
	"eqp_blk": "",
	"screen_location": "",
	"screen_id": "",
	"screen_batch":"",
	"screen_code":"",
	"event_print_count": 0
}
EndResource

Resource screen_wafer //////////////this
{
	"eqp_blk": "DPT09A",
	"screen_print_detail": [
		["location", "print_count", "print_count_max", "reset_flag"],
		["1", "1000", "1000", "0"],
		["2", "1000", "1000", "0"],
		["3", "1000", "1000", "0"],
		["4", "1000", "1000", "0"]
	]
}
EndResource

Resource TX_JSON_Template
{
    "event":"",
    "event_time": "",
	"data": null,
    "metadata":
	{
        "msg_seq_ns": "",
        "msg_seq": 0
    }
}
EndResource


Script GetJSON
(rname, o)
U4 ret;
String s;
GetResource(rname, s, ret);
if (ret)
{
	AppLog("Failed to GetJSON ", rname);
	return ret;
}

JSONParse(o, s, ret);
if (ret)
{
	AppLog("Failed to JSONParse ", s);
}
return ret;
EndScript

Script GetJSONFile
(fname, o)

String s;
return ifexp(StrReadFile(s, fname) || JSONParse(o, s))(-1)(0);

EndScript

Script PutJSONFile
(fname, o, style)

String s;
return ifexp(JSONWrite(o, style, s) || StrWriteFile(s, fname))(-1)(0);

EndScript

Script M_ShowCount
(s)
PrintLn(GetCount(:s));
EndScript

Script M_ShowGlobalName
PString ps;
Reference p;
U0 size;
MapEnumKey(Global, ps, size);
PrintLn("Total Global is ", size);
foreach(p, ps)
{
	PrintLn(p);
}
PrintLn();
EndScript

Script M_ShowGlobal
(s, key, key2, key3)

switch (GetCount(Parameter))
{
case 0
	PrintLn(Global);
	return;
	
case 1
	PrintLn(:s);
	return;
	
case 2
	PrintLn(:s[key]);
	return;
	
case 3
	PrintLn(:s[key][key2]);
	return;
	
case 4
	PrintLn(:s[key][key2][key3]);
	return;
}
EndScript 

Script ShowGCount
(s, key, key2, key3)

switch (GetCount(Parameter))
{
case 0
	PrintLn(GetCount(Global));
	return;
	
case 1
	PrintLn(GetCount(:s));
	return;
	
case 2
	PrintLn(GetCount(:s[key]));
	return;
	
case 3
	PrintLn(GetCount(:s[key][key2]));
	return;
	
case 4
	PrintLn(GetCount(:s[key][key2][key3]));
	return;
}
EndScript 

Script ShowGType
(s, key, key2, key3)

switch (GetCount(Parameter))
{
case 0
	PrintLn(GetType(Global));
	PrintLn(GetActualType(Global));
	return;
	
case 1
	PrintLn(GetType(:s));
	PrintLn(GetActualType(:s));
	return;
	
case 2
	PrintLn(GetType(:s[key]));
	PrintLn(GetActualType(:s[key]));
	return;
	
case 3
	PrintLn(GetType(:s[key][key2]));
	PrintLn(GetActualType(:s[key][key2]));
	return;
	
case 4
	PrintLn(GetType(:s[key][key2][key3]));
	PrintLn(GetActualType(:s[key][key2][key3]));
	return;
}
EndScript 

Script RunFile
(fname)
String s;
U4 ret;
StrReadFile(s, fname, ret);
if (ret == 0)
{
	RunAdhocScript(s);
}
EndScript


Script CKafkaProducer
(name, cb)

String Name = name;
String CB = cb;

Producer@QKafka Producer;
Config@QKafka Config;
CV cv;
U0 EID = 0;
U0 SB = 0;
Object v;

SetName@QKafka(Producer, Name);

Script Send
(topic, msg, sb, key)

I4 ret, rkafka;
String StrError;

CVLock(cv);
if (EID)
{
	sb = Inc(SB);
	
	AppLog("[Topic:",topic,"][EAP:",key,"] SendMsg is ",msg," sb = ", sb);
	
	if (GetCount(Parameter) == 4)
	{
		ProduceEx@QKafka(Producer, topic, key, msg, SB, rkafka, ret);
	}
	else
	{
		Produce@QKafka(Producer, topic, msg, SB, rkafka, ret);
	}
	
	if (ret)
	{
		AppLog("[", Name, "][Error] Send failed.  Produce@QKafka return ", ret);
		CVUnlock(cv);
		return ret;
	}
	
	if (rkafka)
	{
		GetKafkaErrorString@QKafka(rkafka, StrError);
		AppLog("[", Name, "][Error] Produce@QKafka returned ", rkafka, ".  ", StrError);
		CVUnlock(cv);
		return rkafka;
	}
	
	CVUnlock(cv);
	return 0;
}

CVUnlock(cv);
AppLog("[", Name, "][Error] Send is called but message loop is not started");

return -1;

EndScript

Script MessageLoop

Object o;
I4 ops_served, ret;


AAA:

if (TaskGet(100, o))			//if time out (0 means there are request)
{
	CVLock(cv);
	PollProducer@QKafka(Producer, 50, ops_served, ret);
	CVUnlock(cv);
}
else
{
	if (o == "quit")
	{
		return;
	}
}
goto AAA;

EndScript

Script Start

I4 ret;
String StrError;

CVLock(cv);
if (EID)
{
	AppLog("[Bug] ", Name , " called Start again");
	CVUnlock(cv);
	return -1;
}

StartProducer@QKafka(Producer, Config, StrError, CB, ret);
if(ret)
{
	AppLog("[", Name, "] Failed to StartProducer@QKafka, ", ret, ".  ", StrError);
	return -1;
}
	
EID = PCall("MessageLoop");
AppLog("[", Name, "] message loop started with EID = ", EID);
CVUnlock(cv);
return 0;

EndScript

Script Stop
CVLock(cv);
if (EID)
{
	TaskPost(EID, "quit");
	CVUnlock(cv);
	return 0;
}
CVUnlock(cv);
return -1;
EndScript

Script SetConfig
(key, value)

U4 ret;
String StrError;

CVLock(cv);
Set@QKafka(Config, key, value, StrError, ret);
if (ret)
{
	AppLog("[", Name, "][Error] ", ScriptName(), " key = ", key, ", value = ", value, ".  ", StrError);
}
else
{
	AppLog("[", Name, "] SetConfig, key = ", key, ", value = ", value);
}
CVUnlock(cv);
return ret;
EndScript

EndScript

Script DefaultKafkaCallBack
(engine, name, errcode, sb)
if (errcode)
{
	AppLog("[", name, "][Info] Message (", sb, ") not sent, errcode = ", errcode);
}
else
{
	//AppLog("[", name, "][Info] Message (", sb, ") sent");
}

EndScript

Script EAPStarHttpAI
String strB,strC,strRes,msg_seq_ns,url;
U4 ret,msg_seq;
Object J;
Vector vec;

msg_seq=0;
msg_seq_ns=ToAikoTime(FT2QST(CurrentFT()));
StrAssign(url,"http://",:EAP["HTTPServer"]["LocalIP"],":",:EAP["HTTPServer"]["LocalPort"],"/api");

GetResource("EAPStartUp", strB, ret);
JSONParse(J, strB, ret);
J["event_time"]=msg_seq_ns;
J["data"]["url"]=url;
J["data"]["eqp_blk"]="CT";
J["metadata"]["msg_seq_ns"]=msg_seq_ns;
J["metadata"]["msg_seq"]=msg_seq;
VectorPush(vec,"halm_bin");
J["data"]["service"]=vec;
strC="";
JSONWrite(J, 0, strC, ret);
AppLog(ScriptName(), " [Info] ", strC);
PrepareHTTPMESEx(strC,strRes,:EAP["ToolID"]);
AppLog(ScriptName(), " [BackInfo] ", strRes);
EndScript

Script PrepareHTTPMESEx
(s,R,ToolID)
(s,R,ToolID,httpflag)
String out;
String x,y,k;
Reference p1;
U0 index;
String IP;
I4 Port;

if(GetCount(Parameter)==4)
{
	if(MapCheck(:httplinks,httpflag))
	{
		StrAssign(x,:httplinks[httpflag].ip,":",:httplinks[httpflag].port);
		StrAssign(y,"POST ",:httplinks[httpflag].path," HTTP/1.1\x0D\x0A");
		IP=:httplinks[httpflag].ip;
		Port=:httplinks[httpflag].port;
	}
	else
	{
		AppLog("send http ip error");
		return;
	}
}
else
{
	if(GetCount(:httplinks)>0)
	{
		foreach(p1,:httplinks)
		{
			StrAssign(x,p1["ip"],":",p1["port"]);
			StrAssign(y,"POST ",p1["path"]," HTTP/1.1\x0D\x0A");
			IP=p1["ip"];
			Port=p1["port"];
			fbreak;
		}
	}
	else
	{
		AppLog("HttpLink not found");
		return;
	}
}

StrAppend(out, y, 
	"Content-Length: ", StrLen(s), "\x0D\x0A",
	"Host:",x,"\x0D\x0A",
	"EqpID: ", ToolID, "\x0D\x0A",
	"Content-Type: application/json\x0D\x0A\x0D\x0A",
	s);
SendAndWait_HTTP(out, IP, Port,R);
EndScript

Script CheckEAPPhase
(eqpid)
String toolid,strB;
Object J;
U4 ret;

GetResource("EAPPhase", strB, ret);
JSONParse(J, strB, ret);

if(StrLen(eqpid)==6)
{
	toolid=StrSub(eqpid,0,5);
	if(VectorCheck(J["Phase2"],toolid))
	{
		return 2;
	}
	if(VectorCheck(J["Phase3"],toolid))
	{
		return 3;
	}
	return 0;
}

EndScript

Script VectorCheck
(vec, v)
U4 loop = 0;
while(loop < GetCount(vec))
{
	if(v == vec[loop])
	{
		return 1;
	}
	Inc(loop);
}
return 0;
EndScript

Resource EAPPhase
{   
    "Phase2":["DCT01","DCT02","DCT03","DCT04","DCT05","DCT06","DCT07","DCT08","DCT09","DCT10","DCT11","DCT23"],
	"Phase3":["DCT12","DCT13","DCT14","DCT15","DCT16","DCT17","DCT18","DCT19","DCT20","DCT21"]
}
EndResource

Resource S6F12
S6F12
<B 0>.
EndResource

Resource S1F2
S1F2.
EndResource

Resource S1F14
S1F14
<L
	<B 0>
	<L>
>.
EndResource

Resource EstablishComm
S1F13 W
<L>.
EndResource

Resource DisableAllEvent
S2F37 W
<L
  <BOOLEAN F>
  <L>
>.
EndResource

Resource EnableAllEvent
S2F37 W
<L
  <BOOLEAN T>
  <L
>
>.
EndResource

Script CHTTP
(name, ip, port, url, timeout)

String Name = name;

String URL=url;
String IP = ip;
U2 Port = port;
U4 TimeOut = timeout;		//in ms
I4 RCode;

U0 eid = QS_EID();
CV cvX;		//must acquire this lock before any work (to block all waiting user)
CV cv1;		//must acquire this to write to InString
CV cv2;		//must acquire this to write to OutBody, OutHeader, OutRes;

I4 SockState = 0;		//0 not connected, 1 is connected
Object Sock;

String NS = ToAikoTime(FT2QST(CurrentFT()));
U4 Sequence = 0;

String InString;
String OutHeader, OutBody, OutResponse;


Script Connect
ObjCreateEx(Sock, "QSock");
if (QSO_Socket(Sock, 2, 1, 0))
{
	AppLog(Name, " QSO_Socket failed");
	ObjDelete(Sock);
	return -1;
}

if (QSO_Bind(Sock, "0.0.0.0", 0))
{
	AppLog(Name, " QSO_Bind failed");
	return -1;
}

if (QSO_Connect(Sock, IP, Port))
{
	AppLog(Name, " QSO_Connect failed");
	ObjDelete(Sock);
	return -1;
}
AppLog(Name, " connected to ", IP, ":", Port);
SockState = 1;
return 0;
EndScript

Script Disconnect
SockState = 0;
AppLog("QSO_ShutDown ", QSO_ShutDown(Sock, 1));
AppLog("QSO_Close ", QSO_Close(Sock));
ObjDelete(Sock);
AppLog("[Debug] Completed Object Delete");
EndScript

Script HTTP_SendAndWait
(in, h, outRes, outHeader, outBody)

U4 remain;
I4 ret = -1;
String tmp;

CVLock(cvX);

in[3][0] = NS;
in[3][1] = Inc(Sequence);
OutBody = "";

CVLock(cv1);
JSONWrite(in, 0, tmp, ret);
StrAppend(h, "Content-Length: ", StrLen(tmp), "\x0D\x0A\x0D\x0A");
StrAssign(InString, h, tmp);
AppLog("[", Name, "] ", InString);
ret = -1;
RCode = 0;
CVWakeAll(cv1);
CVUnlock(cv1);

CVLock(cv2);
remain = TimeOut;
Again:
switch (RCode)
{
case 0
	remain = CVWaitEx(cv2, remain);
	if (remain)
	{
		goto Again;
	}
	CVUnlock(cv2);
	break;
	
case 1
	CVUnlock(cv2);
	outRes = "XXX";
	outHeader = OutHeader;
	outBody = OutBody;
	ret = 0;
	break;
	
case -1
	CVUnlock(cv2);
	AppLog("[", Name, "] Failure");
}

CVUnlock(cvX);

return ret;
EndScript

Script CheckSock

String s;
U4 event;

if (QSO_Wait(Sock, 20, 1000, event))		//20 = 16 (remote close) + 4 (read)
{
	if (event & 4)
	{
		AppLog("[", Name, "] detected data during CheckSock");
EmptyAgain:
		if (QSO_Recv(Sock, s, 50000))
		{
			AppLog("[", Name, "] QSO_Recv failure.  Closing socket");
			AppLog("[", Name, "] QSO_Close ", QSO_Close(Sock));
			SockState = 0;
		}
		else
		{
			switch (StrLen(s))
			{
			case 50000
				AppLog("[", Name, "] Discarding unwanted data ", Strlen(s), "bytes");
				goto EmptyAgain;
				
			case 0
				AppLog("[", Name, "] Receive 0 length data.  Closing socket");
				AppLog("[", Name, "] QSO_Close ", QSO_Close(Sock));
				SockState = 0;
				break;
				
			default
				AppLog("[", Name, "] Discarding unwanted data ", Strlen(s), "bytes");
				break;
			}
		}
	}
	
	if (event & 16)
	{
		AppLog("[", Name, "] Remote has shutdown.  Closing socket");
		AppLog("[", Name, "] QSO_Close ", QSO_Close(Sock));
		SockState = 0;
	}
}

EndScript

Script MessageLoop

U0 actual_sent;

CVLock(cv1);
Again:
if (StrLen(InString))
{
	if (SockState == 0)
	{
		if (Connect())			//if fail to connect
		{
			InString = "";
			CVLock(cv2);
			NotifyRCode(-1);
			goto Again;
		}
	}
	
	//CheckSock();			//an extra step to ensure there is no buffer in the socket
	
	if (QSO_Send(Sock, InString, actual_sent))
	{
		Disconnect();
		InString = "";
		CVLock(cv2);
		NotifyRCode(-1);
		goto Again;
	}
	
	AppLog(Name, " message sent, size = ", StrLen(InString), ", actual = ", actual_sent);
	HTTP_Receive();
	if (SockState)
	{
		Disconnect();			//an extra step to ensure there is no buffer in the socket
	}
}
CVWait(cv1);
goto Again;

EndScript

Script NotifyRCode
(code)
RCode = code;
CVWakeAll(cv2);
CVUnlock(cv2);
EndScript

Script HTTP_Receive

String s;		//the recv buffer
U4 SplitFlag = 0;
U4 event;
U4 RType = 0;			//0 for not defined, 1 for Content-Length, 2 for chunked, 3 for chunked while parsing body (body length is not yet at the required length)
String R;
U4 len;
U0 pos, pos2;
U0 content_length, offset = 0;
String tmp, tmp2;
String Body;

CVLock(cv2);

XXX:
	if (QSO_Wait(Sock, 20, 5000, event))		//20 = 16 (remote close) + 4 (read)
	{
		AppLog(Name, " error in QSO_Wait");
		Disconnect();
		NotifyRCode(-1);
		return;
	}

	if (event & 1)
	{
		AppLog(Name, " Timeout waiting for response from HTTP server");
		Disconnect();
		NotifyRCode(-1);
		return;
	}
	
	if (event & 2)			//User Event
	{
		AppLog(Name, " unexpected user event");
		Disconnect();
		NotifyRCode(-1);
		return;
	}

	if (event & 4)			//read event
	{
		if (QSO_Recv(Sock, s, 50000))		//the 50000 is a number plug from the air
		{
			AppLog(Name, " QSO_Recv failed");
			Disconnect();
			NotifyRCode(-1);
			return;
		}
		
		if (StrLen(s) == 0)
		{
			AppLog(Name, " received empty data");
			Disconnect();
			NotifyRCode(-1);
			return;
		}
				
		if (SplitFlag)			//this indicate header is complete
		{
			switch (RType)
			{
			case 1			//Content-Length
				StrAppend(OutBody, s);
				if (StrLen(OutBody) == content_length)
				{
					AppLog("Message with content length, ", content_length, " completed");
					NotifyRCode(0);
					return;
				}
				break;
				
			case 2				//chunked
				StrAppend(Body, s);
Chunked2_Again:
				
				if (StrFind(Body, "\x0D\x0A", offset, pos))
				{
					StrSubEx(tmp, Body, offset, pos);
					StrReplace(Body, offset, pos + 2, "");			//remove the line with number
					StrReplace(tmp, 0, 0, StrRepeat(tmp2, "0", 8 - StrLen(tmp)));
					BinaryAssign(len, StrConvertHex(tmp2, tmp));
					len = HiLoByte(len);
					if (len == 0)
					{
						AppLog("Chunked TX completed with data of ", StrLen(OutBody));
						NotifyRCode(1);
						return;
					}
					
					if (StrLen(OutBody) >= len + 2)
					{
						StrAppend(R, StrSub(OutBody, 0, len));
						StrReplace(OutBody, 0, len + 2, "");
						goto Chunked2_Again;
					}
					RType = 3;		//waiting for more body data in chunked
				}
				break;
				
				
			case 3					//chunked while parsing
				StrAppend(OutBody, s);
				if (StrLen(OutBody) >= len + 2)
				{
					StrAppend(R, StrSub(OutBody, 0, len));
					StrReplace(OutBody, 0, len + 2, "");
					RType = 2;
					goto Chunked2_Again;
				}
				break;
			}
			
		}
		else		//header is not complete
		{
			StrAppend(R, s);
			if (StrFind(R, "\x0D\x0A\x0D\x0A", 0, pos))
			{
				SplitFlag = 1;
				StrSubEx(OutHeader, R, 0, pos + 2);
				Body = StrReplace(R, 0, pos + 4, "");
				
				if (StrFind(OutHeader, "Content-Length: ", 0, pos) && StrFind(OutHeader, "\x0D\x0A", pos, pos2))		//Look for line with Content-Length 
				{
					RType = 1;
					StrReplaceR(OutHeader, 0, 2, "");
					content_length = StrSub(OutHeader, pos + 16, pos2 - pos - 16);
					if (StrLen(Body) == content_length)
					{
						OutBody = Body;
						NotifyRCode(1);
						return;
					}
				}
				else
				{
					StrReplaceR(OutHeader, 0, 2, "");		//dirty code before a proper header parser is implemented
				}
				
				if (StrFind(OutHeader, "Transfer-Encoding: ", 0, pos) && StrFind(OutHeader, "chunked", pos, pos2))		//Look for line with chunked
				{
					RType = 2;
					R = "";
Chunked_Again:
					if (StrFind(Body, "\x0D\x0A", offset, pos))
					{
						StrSubEx(tmp, Body, offset, pos);
						StrReplace(Body, offset, pos + 2, "");			//remove the number, and also the 2 new line characters
						StrReplace(tmp, 0, 0, StrRepeat(tmp2, "0", 8 - StrLen(tmp)));		//pad 0 upto 8 characters
						if (StrConvertHex(tmp2, tmp))
						{
							AppLog("[Error] ", ScriptName(), " Unabled to convert hexadecimal number ", tmp);
							return;
						}
						BinaryAssign(len, tmp2);
						len = HiLoByte(len);
						if (len == 0)		//found 0 length
						{
							NotifyRCode(1);
							return;
						}
						
						if (StrLen(Body) >= len + 2)
						{
							StrAppend(OutBody, StrSubEx(tmp, Body, 0, len));
							StrReplace(Body, 0, len + 2, "");
							goto Chunked_Again;
						}

						RType = 3;
					}
				}
				else
				{
					OutBody = Body;
					NotifyRCode(0);
					return;
				}
			}
		}
	}
	
	if (event & 16)		//close event
	{
		AppLog(Name, " unexpected socket closed at remote");
		Disconnect();
		NotifyRCode(-1);
		return;
	}
	
	CheckSock();
	
	goto XXX;

EndScript

EndScript


//			Script FDC_SendStream
//			(data, fdc)
//			
//			static String ns;
//			static U4 sequence;
//			Object j;
//			String Header;
//			String Body;
//			String outRes, outHeader, outBody;
//			if (GetJSON("TX_JSON_Template", j))
//			{
//				return -1;
//			}
//			
//			if (StrLen(ns) == 0)
//			{
//				ns = ToAikoTime(FT2QST(CurrentFT()));
//				sequence = 0;
//			}
//			
//			StrAppend(Header, "Host: ", :fdc.IP, ":", :fdc.Port, "\x0D\x0A");
//			StrAppend(Header, "Accept: */*\x0D\x0A");
//			StrAppend(Header, "Content-Type: application/json\x0D\x0A");
//			
//			j["event"] = "stream_data_collection";
//			j["event_time"] = ToAikoTime(FT2QST(CurrentFT()));
//			j["data"] = data;
//			j["metadata"]["msg_seq_ns"] = ns;
//			j["metadata"]["msg_seq"] = Inc(sequence);
//			
//			if (JSONWrite(j, 0, Body))
//			{
//				AppLog("Failed to JSONWrite in ", ScriptName());
//				return;
//			}
//			
//			if (:fdc.HTTP_SendAndWait(Body, "POST",  "/v1/stream_data_collection", Header, outRes, outHeader, outBody))
//			{
//				AppLog("[Error] ", fdc, " failure at HTTP_SendAndWait");
//			}
//			else
//			{
//				AppLog("[Info] ", outRes, "\x0A", outHeader, "\x0A", outBody);
//			}
//			EndScript


Script FDC_SendStream
(data, fdc)
FDC_SendGeneric(data, fdc, "stream_data_collection");
EndScript

Script FDC_SendBatch
(data, fdc)
FDC_SendGeneric(data, fdc, "batch_data_collection");
EndScript

Script FDC_SendGeneric
(data, fdc, event)

Object j;
String header, outRes, outHeader, outBody;
Reference pPath;

GetUpStreamJSON(:fdc, j, event);
j["data"] = data;

if (IFind(pPath, :fdc.mPath, event) == 0)
{
	AppLog("[Error] Path not defined for ", event);
	return -1;
}

if (HTTPClientHelperEx(:fdc.httpclient, pPath, header, j, outRes, outHeader, outBody))
{
	AppLog("[Error] ", event, " failed");
	return -1;
}

AppLog("[", event, " response] ", outBody);
return 0;
	
EndScript


Resource FDC_Data_Template
{ 
	"eqp_id": "",
	"proc_type": "",
	"eqp_model": "",
	"dcoll_for": "",
	"ppid": null,
	"dc_channel": "",
	"plan_id": "",
	"dc_interval": 0,
	"dcqv": 1.0,
	"start_time": "",
	"end_time": "",
	"duration_s": 0,
	"vid_data":
	[]
}	
EndResource

Resource FDC_Data_Template_Batch
{ 
	"eqp_id": "",
	"proc_type": "DF",
	"eqp_model": "c.Diff X POCl LP 4.000-370-5(5)",
	"dcoll_for": "BATCH",
	"ppid": "",
	"dc_channel": "HSMS",
	"plan_id": "DF_Centrum_Plan",
	"dc_interval": 5,
	"chamber": "",
	"boat_id": "",
	"batch_id": "",
	"pp_step_id": "",
	"pack_id": 0,
	"pack_flag": 0,
	"dcqv": 0.95,
	"start_time": "",
	"end_time": "",
	"duration_s": 30,
	"vid_data":[]
}
EndResource

Resource VID_Data_Template
{
	"vid": "",
	"chamber":"",
	"param_name":"",
	"match_alias": "",
	"data":[]
}
EndResource

Script strLTrim
(s, t)
U0 pos;
pos = StrFindFirstNot(s, t);
if (pos != -1)
{
	StrReplace(s, 0, pos, "");
}
EndScript

Script strRTrim
(s, t)
U0 pos;
pos = StrFindLastNot(s, t);
if (pos != -1)
{
	StrReplace(s, pos + 1, -1, "");
}
EndScript

Script strTrim
(s, t)
strLTrim(s, t);
strRTrim(s, t);
EndScript

Script FillMESInfoGeneric
(wid, J)

Reference p, p2, p3, pCheck;

if (IFind(p, :MapMESKey, wid) && IFind(p2, :MapMESInfo, p) && IFindEx(p3, p2, "data"))
{
	J["comp_id"]		= p3["comp_id"];
	J["wo_id"]			= p3["wo_id"];
	J["prod_code"]		= p3["prod_code"];
	J["prod_id"]		= p3["prod_id"];
	J["prod_name"]		= p3["prod_name"];
	if (IFindEx(pCheck, p3, "tech") && GetActualType(pCheck) != "EOF")
	{
		J["tech"]			= pCheck;
	}
	J["flow_id"]		= p3["flow_id"];
	J["flow_name"]		= p3["flow_name"];
	J["wo_dept_id"]		= p3["wo_dept_id"];
	J["step_name"]		= p3["step_name"];
	J["eqp_blk"]		= p3["eqp_blk"];
	J["recipe"]			= p3["recipe"];	
	J["ppid_plan"]		= p3["ppid_plan"];
	
	if(CompareIMap(J["ppid_actual"], p3["ppid_plan"]))
	{
		J["ppid_ap_same"]="Y";
	}
	else
	{
		J["ppid_ap_same"]="N";
	}
}

EndScript

Script SendCVToKafkaGeneric
(kafka, wid, lane, bflag, proc_hist, ppid, flowio, mapdata)

Reference p;
String key;
Object J;
String currentTime, e;

if (GetJSON("FlowWafer", J))
{
	AppLog("[Fatal] Unable to get JSON resource FlowWafer");
	return;
}
	
currentTime = ToAikoTime(FT2QST(CurrentFT()));

J["event_time"]				= currentTime;

//J["eqp_block_id"]				= StrAssign(e, :EAP["EqpBlockID"], bflag);
//J["eqp_id"]					= :EAP["EqpBlockID"];

J["eqp_block_id"] = ifexp(IMapCheck(:EAP, "eqp_block_id"))(:EAP["eqp_block_id"])(:EAP["EqpBlockID"]);
J["eqp_id"] = ifexp(IMapCheck(:EAP, "eqp_id"))(:EAP["eqp_id"])(:EAP["EqpBlockID"]);

J["fab"]					= :EAP["Fab"];

if (IFind(p, :mapCW, wid))
{
	J["data"]["wafer_id"] = p;
}
else
{
	AppLog("[Fatal] ", wid, " not found in mapCW at ", ScriptName());
}

J["data"]["lane"]			= lane;		
J["data"]["ppid_actual"]	= :ppid;
J["data"]["flow_io"]		= flowio;
J["data"]["flow_out_time"]	= currentTime;
J["data"]["rec_time"]		= currentTime;
J["data"]["proc_hist"]		= proc_hist;

IBegin(p, mapdata, key);
while (IIsEnd(p) == 0)
{
	J["data"][key] = p;
	IInc(p);
}

FillMESInfoGeneric(wid, J["data"]);
PrepareKafka(J, :EAP["ToolID"], "eap-collection-wafer-cv", kafka);

EndScript

Script WCS_UpdatePortCache
(dst, port, count)
Object J;
IMap im;

String header, outRes, outHeader, outBody;

GetUpStreamJSON(:dst, J, "update_port_cache_count");
IMapPush(im, port, count);
J[2] = im;
if (HTTPClientHelperEx(:dst.httpclient, "/rtd/wcs/v1/eap_update", header, J, outRes, outHeader, outBody))
{
	AppLog("WCS_UpdatePortCache failed to perform transaction");
	return -1;
}
return 0;
EndScript

Script WCS_UpdatePortSubCache
(dst, port, count)
Object J;
IMap im;

String header, outRes, outHeader, outBody;

GetUpStreamJSON(:dst, J, "update_port_sub_cache_count");
IMapPush(im, port, count);
J[2] = im;
if (HTTPClientHelperEx(:dst.httpclient, "/rtd/wcs/v1/eap_update", header, J, outRes, outHeader, outBody))
{
	AppLog("WCS_UpdatePortSubCache failed to perform transaction");
	return -1;
}
return 0;
EndScript

Script WCS_UpdatePortStatus
(dst, port, status)
Object J;
IMap im;

String header, outRes, outHeader, outBody;

GetUpStreamJSON(:dst, J, "update_port_status");
IMapPush(im, port, status);
J[2] = im;
if (HTTPClientHelperEx(:dst.httpclient, "/rtd/wcs/v1/eap_update", header, J, outRes, outHeader, outBody))
{
	AppLog("WCS_UpdatePortStatus failed to perform transaction");
	return -1;
}
return 0;
EndScript

Script RTD_UpdateCassInfo
(dst, rfid, info, time, qty, type)
Object J;
IMap im;
U0 i;
Reference p, pdst;
String header, outRes, outHeader, outBody;

GetUpStreamJSON(:dst, J, "update_cass_info");

ObjCreateEx(J["data"], "IMap");
foreach (p, rfid, i)
{
	IMapPushExP(J["data"], p, "IMap", pdst);
	IMapPush(pdst, "port", info[i]);
	IMapPush(pdst, "time", time[i]);
	IMapPush(pdst, "qty", qty[i]);
	IMapPush(pdst, "type", type[i]);
}


if (HTTPClientHelperEx(:dst.httpclient, "/rtd/wcs/v1/eap_update_cass", header, J, outRes, outHeader, outBody))
{
	AppLog("RTD_UpdateCassInfo failed to perform transaction");
	return -1;
}
return 0;
EndScript

Script MES_PackerStatusChange
(m, lot_id, eqp_id, r_object)

Object J;

String event = "packer_status_change";
String header;
String outRes, outHeader, outBody;
Reference pData, pPath;

GetUpStreamJSON(:m, J, event, event);

RefSet(pData, J["data"]);
pData["lot_id"] = lot_id;
pData["eqp_id"] = eqp_id;

if (IFind(pPath, :m.mPath, event) == 0)
{
	AppLog("[Error] Path not defined for ", event);
	return -1;
}

if (HTTPClientHelperEx(:m.httpclient, pPath, header, J, outRes, outHeader, outBody))
{
	AppLog("[Error] MES_PackerStatusChange failed");
	return -1;
}

if (JSONParse(r_object, outBody))
{
	AppLog("[Error] Failed in JSONParse\x0A", outBody);
}

return 0;

EndScript

Script MES_WaferStart
(mes, car, qty, k, ppid, boxid1, boxid2, wids, R)

Vector v;
String outRes, outHeader, outBody;
String header;
U4 ret,loop;
Object J, J2;

if (GetJSON("WaferStart", J))
{
	return -1;
}

J["cass_id"] = car;
J["eqp_blk"] = :EAP["ToolID"];
J["lane"] = :k.K;
J["control_mode"] = :k.Mode;
J["rfid1"] = boxid1;
J["rfid2"] = boxid2;
J["ppid"] = ppid;
:k.Generate(v, qty);

if(GetCount(wids) != qty)
{
	AppLog("[Error] qty wids count fail ", mes);
	return 1;
}

loop = 0;	
while(loop < GetCount(wids))
{
	MapAdd(:mapCW, wids[loop], v[loop]);
	Inc(loop);
}

J["wafers"] = v;

if (HTTPClientHelper(mes, "/api/flow/wafer_start", header, FillJSON("wafer_start", J), outRes, outHeader, outBody))
{
	AppLog("[Error] Failed to perform HTTPClientHelper at ", ScriptName());
	return -1;
}

if (JSONParse(J2, outBody))
{
	AppLog("[Error] Failed to parse JSON at ", ScriptName(), "\x0A", outBody);
	return -1;
}
:MES.HandleFlowInReply(J2,wids);

R = outBody;
return 0;
EndScript

Script FillJSON
(event,J)
Object O;

if (GetJSON("TX_JSON_Template", O))
{
	return -1;
}
O["event"] = event;
O["event_time"] = ToAikoTime(FT2QST(CurrentFT()));
O["data"] = J;
return O;
EndScript

Script VectorToPU4
(vec)
U4 loop;
PU4 p;
loop=0;
while(loop<GetCount(vec))
{
	if (GetType(vec[loop]) == "U4")
	{
		VectorPush(p,vec[loop]);
	}
	else
	{
		VectorPush(p,ToU4(vec[loop]));
	}
	Inc(loop);
}
return p;
EndScript

Script SubVector
(vec,s,e)
U4 loop;
Vector v;
loop=s-1;
if(e<=GetCount(vec)&&s<=e)
{
	while(loop<e)
	{
		VectorPush(v,vec[loop]);
		Inc(loop);
	}
}
return v;
EndScript

Script LogClean
(period, config)

Reference pJob, pFile;
Vector vFile;
U8 current;
PU8 mtime;
U0 i;
String tmp;

Again:
current = CurrentFT();
foreach(pJob, config)
{
	if (Pattern(pJob[0], "Log/*"))			//can only remove those in the Log folder
	{
		Sys_FileList(vFile, pJob[0], mtime);
		foreach(pFile, vFile, i)
		{
			if (Pattern(pFile, StrAssign(tmp, pJob[0], pJob[1])) && current > ToU8(pJob[2]) * 36000000000U8 + mtime[i])
			{
				if (Sys_RemoveFile(pFile))
				{
					AppLog("[", ScriptName(), "] File ", pFile, " is removed");
				}
				else
				{
					AppLog("[", ScriptName(), "] Failed to remove file ", pFile);
				}
				
			}
		}
	}
}
Sleep(period * 1000);
goto Again;

EndScript

Script LogCompress
(period, config)

Reference pJob, pFile;
Vector vFile;
U8 current;
PU8 mtime;
U0 i;
String tmp;

Again:
current = CurrentFT();
foreach(pJob, config)
{
	if (Pattern(pJob[0], "Log/*"))			//can only remove those in the Log folder
	{
		Sys_FileList(vFile, pJob[0], mtime);
		foreach(pFile, vFile, i)
		{
			if (Pattern(pFile, StrAssign(tmp, pJob[0], pJob[1])) && current > ToU8(pJob[2]) * 36000000000U8 + mtime[i])
			{
				StrAssign(tmp, "gzip ", pFile);
				Sys_System(tmp);
				AppLog("[", ScriptName(), "]", tmp);
			}
		}
	}
}
Sleep(period * 1000);
goto Again;

EndScript

Script GetFT
(s)

U8 x, y = 0;
String tmp = s;
String tmp2;
F8 f8;
U0 pos;
if (StrFind(tmp, "+", 0, pos))
{
	StrReplace(tmp, pos, 20, "");
	PrintLn(tmp);
	PrintLn(pos);
}

if (Pattern(tmp, "####-##-## ##:##:##*") || Pattern(tmp, "####/##/## ##:##:##*") || Pattern(tmp, "####.##.## ##:##:##*"))
{
	StrReplace(tmp, 4, 1, "");
	StrReplace(tmp, 6, 1, "");
	StrReplace(tmp, 8, 1, "");
	StrReplace(tmp, 10, 1, "");
	StrReplace(tmp, 12, 1, "");

	StrSubEx(tmp2, tmp, 14);
	x = StrSub(tmp, 0, 14);
	if (StrLen(tmp2))
	{
		if (Pattern(tmp2, ".*") && SafeConvert(f8, tmp2))
		{
			y = f8 * 100000;
			return QST2FT(x * 100000 + y);
		}
		return 0;
	}
	return QST2FT(x * 100000);
}
elseif (Pattern(tmp, "####/##/##") || Pattern(tmp, "####-##-##") || Pattern(tmp, "####.##.##"))
{
	StrReplace(tmp, 4, 1, "");
	StrReplace(tmp, 6, 1, "");
	return QST2FT(ToU8(tmp) * 100000000000U8);
}
elseif (Pattern(tmp, "####-##-## ##:##"))			//yyyy-MM-dd HH:mm
{
	StrReplace(tmp, 4, 1, "");
	StrReplace(tmp, 6, 1, "");
	StrReplace(tmp, 8, 1, "");
	StrReplace(tmp, 10, 1, "");
	return QST2FT(ToU8(tmp) * 10000000);
}
elseif (Pattern(tmp, "##############"))				//yyyyMMddHHmmss
{
	return QST2FT(ToU8(tmp) * 100000);
}
elseif (Pattern(tmp, "#################"))			//yyyyMMddHHmmssSSS
{
	return QST2FT(ToU8(tmp) * 100);
}
elseif (Pattern(tmp, "########"))
{
	return QST2FT(ToU8(tmp) * 100000000000U8);
}
elseif (Pattern(tmp, "####年##月##日 ##时##分##秒"))
{
	StrReplace(tmp, "年", "");
	StrReplace(tmp, "月", "");
	StrReplace(tmp, "日", "");
	StrReplace(tmp, "时", "");
	StrReplace(tmp, "分", "");
	StrReplace(tmp, "秒", "");
	StrReplace(tmp, " ", "");
	return QST2FT(ToU8(tmp) * 100000U8);
}

return 0;

EndScript


Script CreateGhost
(wid, pos, map_in, wgen, t, f)

U4 ghost = Inc(:GhostWIDStart);

Vector tmpv;

StoreWaferID();

:wgen.Generate(tmpv, 1);
MapAdd(:mapCW, ghost, tmpv[0]);
MapAddEx(:MapWafer, ghost, "WaferInfo", ghost, wid, pos, "", 0);
MapAddEx(:map_in, wid, "CMapIn", map_in, wid, ghost, t, f);
AppLog("[Ghost] Entry added to ", map_in, " at ", pos, ", ", wid, "<->", ghost, ", size = ", GetCount(:map_in));
return ghost;
EndScript

Script CreateGhostEx
(wid, pos, map_in, wgen, t, f, ft)

U4 ghost = Inc(:GhostWIDStart);
Vector tmpv;
StoreWaferID();

:wgen.Generate(tmpv, 1);
MapAdd(:mapCW, ghost, tmpv[0]);
MapAddEx(:MapWafer, ghost, "WaferInfo", ghost, wid, pos, "", 0);
MapAddEx(:map_in, wid, "CMapIn", map_in, wid, ghost, t, f, ft);
AppLog("[Ghost] Entry added to ", map_in, " at ", pos, ", ", wid, "<->", ghost, ", size = ", GetCount(:map_in));
return ghost;
EndScript

Script CMapIn
(map_in, local_wid, xx_wid, t, f, ft)

String WaferID = xx_wid;
String TimerID;

if (GetCount(Parameter) == 6)
{
	TimerID = :Timer.AddTimerEx(map_in, local_wid, t, f, ft);
}
else
{
	TimerID = :Timer.AddTimer(map_in, local_wid, t, f);
}


EndScript

Script CMapInG
(map_in, local_wid, map_in_value, t, f)

Object Value = map_in_value;
String TimerID = :Timer.AddTimer(map_in, local_wid, t, f);

EndScript

Script WaferInfoUnit
(wid, pos, t)
String WID = wid;
String Location = pos;
String InTime = t;
String OutTime = t;
EndScript

Script WaferInfo
(key_wid, local_wid, pos, rfid, inslot)

String RFID = rfid;
String EIDBoxId;
U4 InSlotID = inslot + 1;

IMap History;
U8 CreateTimeTick = CurrentFT();
U4 WID = key_wid;						//Needed when preparing the Kafka message for wafer cv

IMapPushEx(History, pos, "WaferInfoUnit", local_wid, pos, ToAikoTime(FT2QST(CreateTimeTick)));

Script Stamp
(wid, pos)

String t = ToAikoTime(FT2QST(CurrentFT()));
Reference p;

if (IFindEx(p, History, pos))
{
	AppLog("[Warning] Duplicate location stamp ", pos, " on wafer ", wid, "\x0A", History);
}
else
{
	History[GetCount(History) - 1].OutTime = t;
	IMapPushEx(History, pos, "WaferInfoUnit", wid, pos, t);
}

EndScript

Script StampEx
(wid, pos, ft)

String t = ToAikoTime(FT2QST(ft));
Reference p;

if (IFindEx(p, History, pos))
{
	AppLog("[Warning] Duplicate location stamp ", pos, " on wafer ", wid, "\x0A", History);
}
else
{
	History[GetCount(History) - 1].OutTime = t;
	IMapPushEx(History, pos, "WaferInfoUnit", wid, pos, t);
}

EndScript

Script GenerateCV
(v)

Reference p, pHist;

VectorClear(v);

VectorPushExP(v, "Vector", p);
VectorPush(p,"loc");
VectorPush(p, "w_id");
VectorPush(p, "i_time");
VectorPush(p, "o_time");

foreach(pHist, History)
{
	VectorPushExP(v, "Vector", p);
	VectorPush(p, pHist.Location);
	VectorPush(p, pHist.WID);
	VectorPush(p, pHist.InTime);
	VectorPush(p, pHist.OutTime);
}

EndScript

Script ClearHistoryExceptLast
U0 n = GetCount(History);
while (n > 1)
{
	IMapRemove(History, 0);
	Dec(n);
}
EndScript

EndScript

Script StampHistory
(wid, pos, map_in, wgen, t, f)

//must already be in CVLock

String tid;
Reference pMapIn, pMapWafer;


if (IFind(pMapIn, :map_in, wid)) 
{
	if (IFind(pMapWafer, :MapWafer, pMapIn.WaferID))
	{
		tid = pMapIn.TimerID;
		pMapWafer.Stamp(wid, pos);
		pMapIn.TimerID = :Timer.UpdateTimer(tid, map_in, wid, t, f);
	}
	else
	{
		AppLog("[Fatal] Wafer ", wid, " found in ", map_in, " but ", pMapIn.WaferID, " not in MapWafer");
	}
}
else
{
	CreateGhost(wid, pos, map_in, wgen, t, f);
}

EndScript

Script StampHistoryEx
(wid, pos, map_in, wgen, t, f, ft)

//must already be in CVLock

String tid;
Reference pMapIn, pMapWafer;

if (IFind(pMapIn, :map_in, wid)) 
{
	if (IFind(pMapWafer, :MapWafer, pMapIn.WaferID))
	{
		tid = pMapIn.TimerID;
		pMapWafer.StampEx(wid, pos, ft);
		pMapIn.TimerID = :Timer.UpdateTimerEx(tid, map_in, wid, t, f, ft);
	}
	else
	{
		AppLog("[Fatal] Wafer ", wid, " found in ", map_in, " but ", pMapIn.WaferID, " not in MapWafer");
	}
}
else
{
	CreateGhost(wid, pos, map_in, wgen, t, f);
}

EndScript

Script CTimerData
(map_in, key, function)

String Structure = map_in;
String Key = key;
String Function = function;

EndScript

Script CTimer

CV cv;
Map m;

Script AddTimer
(map_in, key, t, f)

//t is in seconds

CVLock(cv);
U8 ft = CurrentFT() + t * 10000000U8;
while (MapCheck(m, ft))		//this loop is to make sure ft is unique as key to the map m
{
	Inc(ft);
}
MapAddEx(m, ft, "CTimerData", map_in, key, f);
CheckTimeOut();
CVUnlock(cv);
return ft;
EndScript

Script AddTimerEx
(map_in, key, t, f, ftx)

//t is in seconds

CVLock(cv);
U8 ft = ftx + t * 10000000U8;
while (MapCheck(m, ft))		//this loop is to make sure ft is unique as key to the map m
{
	Inc(ft);
}
MapAddEx(m, ft, "CTimerData", map_in, key, f);
CheckTimeOut();
CVUnlock(cv);
return ft;
EndScript

Script UpdateTimer
(tid, map_in, key, t, f)

CVLock(cv);
MapRemove(m, tid);
U8 ft = CurrentFT() + t * 10000000U8;
while (MapCheck(m, ft))		//this loop is to make sure ft is unique as key to the map m
{
	Inc(ft);
}
MapAddEx(m, ft, "CTimerData", map_in, key, f);
CheckTimeOut();
CVUnlock(cv);
return ft;
EndScript

Script UpdateTimerEx
(tid, map_in, key, t, f, ftx)

CVLock(cv);
MapRemove(m, tid);
U8 ft = ftx + t * 10000000U8;
while (MapCheck(m, ft))		//this loop is to make sure ft is unique as key to the map m
{
	Inc(ft);
}
MapAddEx(m, ft, "CTimerData", map_in, key, f);
CheckTimeOut();
CVUnlock(cv);
return ft;
EndScript

Script RemoveTimer
(tid)

CVLock(cv);
MapRemove(m, tid);
CVUnlock(cv);

EndScript

Script CheckTimeOut
Reference p;
U8 current = CurrentFT();
String keytime;
String s, k, f;

IBegin(p, m, keytime);
Again:
if (IIsEnd(p) == 0 && ToU8(keytime) < current)
{
	s = p.Structure;
	k = p.Key;		//k is the key in map_in
	f = p.Function;

	ThreadPost(:TimeOutHandling, f, s, k);
	IDelete(p);
	goto Again;
}

EndScript

EndScript

Script GenerateWaferID
(v, n)

//assume caller has perform CVLock

U0 loop = 0;
while (loop < n)
{
	VectorPush(v, Inc(:WIDStart));
	Inc(loop);
}

StoreWaferID();

EndScript

Script StoreWaferID
Object o;
ObjCreateEx(o, "IMap");
IMapPush(o, "WaferID", :WIDStart);
IMapPush(o, "GhostWaferID", :GhostWIDStart);
if (PutJSONFile("WaferID.json", o, 0))
{
	AppLog("[Error] Failed to write to WaferID.json");
}	
EndScript

Script GetStoreWaferID
Object o;
if (GetJSONFile("WaferID.json", o))
{
	GCreate("WIDStart", 1000000000U4);
	GCreate("GhostWIDStart", 2000000000U4);
}
else
{
	GCreate("WIDStart", ToU4(o["WaferID"]));
	GCreate("GhostWIDStart", ToU4(o["GhostWaferID"]));
}

EndScript

Script AddMapIn
(map_in, local_wid, xx_wid, t, f)

MapAddEx(:map_in, local_wid, "CMapIn", map_in, local_wid, xx_wid, t, f);

EndScript

Script RemoveMapIn
(map_in, local_wid)

Reference p;

if (IFind(p, :map_in, local_wid))
{
	:Timer.RemoveTimer(p.TimerID);
	IDelete(p);
}

EndScript

Script RemoveMapInEx
(p)
	:Timer.RemoveTimer(p.TimerID);
	IDelete(p);
EndScript


Script CUpStream
(name)

String Name = name;
Object httpclient;
String ns = ToAikoTime(FT2QST(CurrentFT()));
String http_version = "HTTP/1.1";
U8 seq = 0;
CV cv;
Map mPath;

Script Set
(im)

U4 timeout = 5000;
String ip;
U4 port;
Reference p, p2;
U0 i, i2;
String key, tmpkey;


PrintLn("In ", Name, "\x0A", im);
foreach (p, im, i, key)
{
	switch (key)
	{
	case "RemoteIP"
	case "Remote IP"
		ip = p;
		break;
		
	case "RemotePort"
	case "Remote port"
		port = p;
		break;
		
	case "HTTP_version"
		http_version = p;
		break;
		
	case "TimeOut"
		timeout = p;
		break;
		
	case "Path"
		foreach(p2, p, i2, tmpkey)
		{
			MapAdd(mPath, tmpkey, p2);
		}
		break;
		
	default
		AppLog("[Error] Unknown configuration, ", key);
		break;
	}
}

ObjCreateEx(httpclient, "CHTTPClient", Name, ip, port, timeout, http_version);

EndScript

EndScript


Script HandleFlowInReply
(J, vWID)

U8 curFT = CurrentFT();
Reference p;

CVLock(:CVHTTP);

ClearOldMESInfo();

while (MapCheck(:MapMESInfo, curFT))
{
	Inc(curFT);
}
MapAdd(:MapMESInfo, curFT, J);
AppLog("[MapMESInfo] Added ", curFT, ", size is ", GetCount(:MapMESInfo), "\x0A", J);

foreach(p, vWID)
{
	if(MapCheck(:MapMESKey, p))
	{
		AppLog("[MapMESKey] Wafer id ", p, " has entry in MapMESKey, value is ", :MapMESKey[p], ", updating to ", curFT);
		:MapMESKey[p] = curFT;
	}
	else
	{
		MapAdd(:MapMESKey, p, curFT);
		AppLog("[MapMESKey] Added ", p, "<->", curFT, ", size = ", GetCount(:MapMESKey));
	}
}

CVUnlock(:CVHTTP);
EndScript

Script ClearOldMESInfo
U4 loop = 0, count;
U8 CurTimeTick = CurrentFT();
PString keys;

MapEnumKey(:MapMESInfo, keys, count);

	while (loop < count)
	{
		if(CurTimeTick - ToU8(keys[loop]) > 10000000U8*60*60*24) //24 hour
		{	
			MapRemove(:MapMESInfo, keys[loop]);
		}
		Inc(loop);
	}
		
EndScript